Многопоточность в браузере. Модель акторов —...

37

Upload: yandex

Post on 30-Nov-2014

321 views

Category:

Technology


1 download

DESCRIPTION

Я расскажу о том, как наладить многопоточное взаимодействие в браузере без использования мьютексов и условных переменных. Доклад будет интересен тем, кто пишет на языке C++ и разрабатывает многопоточные сервисы и приложения.

TRANSCRIPT

Page 1: Многопоточность в браузере. Модель акторов — Константин Крамлих
Page 2: Многопоточность в браузере. Модель акторов — Константин Крамлих

Многопоточность в браузере. Модель акторов Константин Крамлих

Page 3: Многопоточность в браузере. Модель акторов — Константин Крамлих
Page 4: Многопоточность в браузере. Модель акторов — Константин Крамлих

Инициализируем плеер

void Player::Init() { OpenFile(); InitializeDecoder(); InitializeRenderer(); StartDecodeLoop(); StartDisplayLoop(); }

Page 5: Многопоточность в браузере. Модель акторов — Константин Крамлих

Читаем и показываем кадры

 // Producer thread void Player::DecodeLoop() { ... while (!end) { ... decoder->ReadFrame(&frame); queue->Push(frame); ... } }

 // Consumer thread void Player::DisplayLoop() { while (!end) { ... queue->Pop(&frame); if (!frame) continue; end = frame->IsLastFrame(); ShowFrame(frame); ... }  }  

Page 6: Многопоточность в браузере. Модель акторов — Константин Крамлих

Почему так нельзя

▌  Греем воздух

▌  Ненужная очередь

▌  Мьютексы/Условные переменные для синхронизации

Page 7: Многопоточность в браузере. Модель акторов — Константин Крамлих

8

Page 8: Многопоточность в браузере. Модель акторов — Константин Крамлих

9

Page 9: Многопоточность в браузере. Модель акторов — Константин Крамлих

Требования к браузеру

▌  Отзывчивый UI

▌  Скорость работы

▌  Выполнение большого количества задач одновременно

10

Page 10: Многопоточность в браузере. Модель акторов — Константин Крамлих

Основные потоки в браузере

▌  UI thread

▌  IO thread

▌  File thread

▌  DB thread

▌  SafeBrowsing thread

▌  History thread

11

Page 11: Многопоточность в браузере. Модель акторов — Константин Крамлих

12

Actor Actor

Actor Actor

Main thread

Page 12: Многопоточность в браузере. Модель акторов — Константин Крамлих

Важные классы в Chromium

▌  Callback & Closure

▌  TaskRunner & SequencedTaskRunner & SingleThreadTaskRunner

▌  MessageLoop & MessageLoopProxy

▌  Thread & PlatformThread

▌  BrowserThread

13

Page 13: Многопоточность в браузере. Модель акторов — Константин Крамлих

Callback & Closure

void DoSomeJob(base::Callback<void (bool)> result_cb); void OnJobCompleted(bool success); void Foo() { DoSomeJob(base::Bind(&OnJobCompleted)); } void OnJobCompletedExtraData(const std::string& extra_data, bool success); void Bar() { DoSomeJob(base::Bind(&OnJobCompletedWithExtraData, "Called from Bar")); }

14

Page 14: Многопоточность в браузере. Модель акторов — Константин Крамлих

Почему не std::function

▌  std::function еще не было

▌  Функционал std::function шире

▌  std::function делает копию внутреннего состояния при копировании

▌  Неявные касты вида std::function<int(int)> -> std::function<void(int)>

Page 15: Многопоточность в браузере. Модель акторов — Константин Крамлих

MessageLoop & MessageLoopProxy

void DoSomeJob(base::Callback<void (bool)> result_cb); void OnJobCompleted(bool success); void Bar() { MessageLoopProxy::current()->PostTask(

FROM_HERE, base::Bind(&DoSomeJob(base::Bind(&OnJobCompleted)))); }

16

Page 16: Многопоточность в браузере. Модель акторов — Константин Крамлих

Thread & PlatformThread

void Bar(); void Foo() { base::Thread thread("MyWorkerThread"); thread.message_loop_proxy()->PostTask(

FROM_HERE, base::Bind(&Bar));

}

Page 17: Многопоточность в браузере. Модель акторов — Константин Крамлих

BrowserThread

void Foo(int); void Bar() { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&Foo, 42)); }

Page 18: Многопоточность в браузере. Модель акторов — Константин Крамлих

base::Bind для методов классов

class SomeClass : public base::RefCountedThreadSafe<SomeClass> { public: void A() {}; void B() { base::Callback<void()> cb = base::Bind(&Foo::A, this); cb.Run(); } };

Page 19: Многопоточность в браузере. Модель акторов — Константин Крамлих

base::Bind для методов классов

class SomeClass { public: void A() {}; void B() { base::Callback<void()> cb = base::Bind(&Foo::A, base::Unretained(this)); cb.Run(); } };

Page 20: Многопоточность в браузере. Модель акторов — Константин Крамлих

Удаление на нужном потоке

class SomeClass : public base::RefCountedThreadSafe<SomeClass, BrowserThread::DeleteOnIOThread> { ... }

Page 21: Многопоточность в браузере. Модель акторов — Константин Крамлих

Обнаружение опасности void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult( const GURL& url, SBThreatType threat_type, const std::string& metadata) { ... SafeBrowsingUIManager::UnsafeResource resource; resource.url = url; ... resource.callback = base::Bind( &SafeBrowsingResourceThrottle::OnBlockingPageComplete, AsWeakPtr()); content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage, AsWeakPtr(), ui_manager_, resource)); }

Page 22: Многопоточность в браузере. Модель акторов — Константин Крамлих

Блокировка страницы void SafeBrowsingUIManager::DisplayBlockingPage( const UnsafeResource& resource) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ... if (IsWhitelisted(resource)) { if (!resource.callback.is_null()) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(resource.callback, true)); } return; } ... SafeBrowsingBlockingPage::ShowBlockingPage(this, resource); }

Page 23: Многопоточность в браузере. Модель акторов — Константин Крамлих

Завершение блокировки void SafeBrowsingUIManager::OnBlockingPageDone( const std::vector<UnsafeResource>& resources, bool proceed) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); for (std::vector<UnsafeResource>::const_iterator iter = resources.begin(); iter != resources.end(); ++iter) { const UnsafeResource& resource = *iter; if (!resource.callback.is_null()) resource.callback.Run(proceed); if (proceed) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&SafeBrowsingUIManager::UpdateWhitelist, this, resource)); } } }

Page 24: Многопоточность в браузере. Модель акторов — Константин Крамлих

Выводы

▌  Данные привязаны к конкретным потокам

▌  Нет нужды в мьютексах

▌  Проверяем, что работаем на нужном потоке

Page 25: Многопоточность в браузере. Модель акторов — Константин Крамлих

Привязка к нужному потоку #define BIND_TO_RENDER_LOOP(function) \ (DCHECK(main_task_runner_->BelongsToCurrentThread()), \ BindToCurrentLoop(base::Bind(function, AsWeakPtr()))) void WebMediaPlayerImpl::StartPipeline() { DCHECK(main_task_runner_->BelongsToCurrentThread()); ... pipeline_.Start( demuxer_.get(), CreateRenderer(), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError), BIND_TO_RENDER_LOOP1(&WebMediaPlayerImpl::OnPipelineSeeked, false), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineMetadata), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingStateChanged), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChanged), BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnAddTextTrack)); } 26

Page 26: Многопоточность в браузере. Модель акторов — Константин Крамлих

Неявное использование на другом потоке

void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) { DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ”; DCHECK(task_runner_->BelongsToCurrentThread()); buffering_state_cb_.Run(new_buffering_state); }

Page 27: Многопоточность в браузере. Модель акторов — Константин Крамлих

Скрытие потоков исполнения virtual void QueryForMatches( const std::set<SourceFrameRef>& candidates, const MatchesCallback& results_callback) OVERRIDE { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&MuteDestination::QueryForMatchesOnUIThread, this, candidates, media::BindToCurrentLoop(results_callback))); } void QueryForMatchesOnUIThread(const std::set<SourceFrameRef>& candidates, const MatchesCallback& results_callback) { ... results_callback.Run(matches); }

Page 28: Многопоточность в браузере. Модель акторов — Константин Крамлих

Выводы

▌  Не нужно думать о том, на каком потоке нужно дергать коллбеки

▌  Скрываем информацию об исполняющем потоке

Page 29: Многопоточность в браузере. Модель акторов — Константин Крамлих

Инициализируем плеер void Player::Init() { OpenFile(); InitializeDecoder(); InitializeRenderer(); StartDecodeLoop(); StartDisplayLoop(); }

30

Page 30: Многопоточность в браузере. Модель акторов — Константин Крамлих

Читаем и показываем кадры  // Producer thread void Player::DecodeLoop() { ... while (!end) { ... decoder->ReadFrame(&frame); queue->Push(frame); ... } }

 // Consumer thread void Player::DisplayLoop() { while (!end) { ... queue->Pop(&frame); if (!frame) continue; end = frame->IsLastFrame(); ShowFrame(frame); ... }  }  

Page 31: Многопоточность в браузере. Модель акторов — Константин Крамлих

Инициализируем плеер

void Player::Init() { OpenFile(); base::Callback<void(Frame)> show_frame_cb = Bind(&ShowFrame, this); InitializeDecoder(BindToCurrentLoop(show_frame_cb)); InitializeRenderer(); StartDecodeLoop(); }

Page 32: Многопоточность в браузере. Модель акторов — Константин Крамлих

Читаем и отдаем кадры // Producer thread void Decoder::DecodeLoop() { ... while (!end) { ... decoder->ReadFrame(&frame); frame_ready_cb.Run(frame); ... } }

Page 33: Многопоточность в браузере. Модель акторов — Константин Крамлих

class Account { double balance; int id; void withdraw(double amount) { balance -= amount; } void deposit(double amount) { balance += amount; } void transfer(Account from, Account to, double amount) { sync(from); sync(to); from.withdraw(amount); to.deposit(amount); release(to); release(from); } }

Избегаем dedlock-и

Page 34: Многопоточность в браузере. Модель акторов — Константин Крамлих

Плюсы

▌  Данные привязаны к конкретным потокам, не требуются примитивы синхронизации

▌  Можно скрыть знание того, на каком потоке будет выполняться задача, а также поток выполнения коллбека

▌  Проверяем поток, на котором происходит выполнение

Page 35: Многопоточность в браузере. Модель акторов — Константин Крамлих

Минусы

▌  Код должен быть асинхронным

▌  Теряем возможность использования исключений

▌  В некоторых случаях использование мьютексов может быть быстрее

36

Page 36: Многопоточность в браузере. Модель акторов — Константин Крамлих

Полезные ссылки

▌  http://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures

▌  http://www.chromium.org/developers/design-documents/threading

▌  https://en.wikipedia.org/wiki/Actor_model

37

Page 37: Многопоточность в браузере. Модель акторов — Константин Крамлих

Спасибо за внимание!

38