狠狠撸
Search
Submit Search
Effective Modern C++ study group Item39
?
Download as PPTX, PDF
?
1 like
?
1,153 views
Takatoshi Kondo
Effective Modern C++勉強会発表資料 https://github.com/herumi/emcjp
Read less
Read more
1 of 18
Download now
Download to read offline
More Related Content
Effective Modern C++ study group Item39
1.
Effective Modern C++
勉強会 Item39 近藤 貴俊 2015/10/4 1
2.
今回紹介するItem ? Item 39:Consider
void futures for one-shot event communication. ? ワンショットイベントにvoid futureを使うことを 考えてみよう。 2015/10/4 2
3.
2015/10/4 3 std::condition_variable cv; std::mutex
m; cv.notify_one(); Item39 ... { std::unique_lock<std::mutex> lk(m); cv.wait(lk); ... } ... イベント用条件変数 cvと一緒に使うmutex cvを通してイベント通知 イベントを通知する側 イベントを受ける側 mutexをlock ここで通知を待つ イベントを受けての処理(mはlock中) イベントを受けての処理(mはlock解除されている) このコードには問題がある
4.
2015/10/4 4 std::condition_variable cv; std::mutex
m; cv.notify_one(); Item39 ... { std::unique_lock<std::mutex> lk(m); cv.wait(lk); ... } ... イベント用条件変数 cvと一緒に使うmutex cvを通してイベント通知 イベントを通知する側 イベントを受ける側 mutexをlock ここで通知を待つ イベントを受けての処理(mはlock中) イベントを受けての処理(mはlock解除されている) 問題その1 1 2 3 3 でブロックしてしまう
5.
2015/10/4 5 std::condition_variable cv; std::mutex
m; 完了しておくべき処理A cv.notify_one(); Item39 ... { std::unique_lock<std::mutex> lk(m); cv.wait(lk); 処理Aが完了していることを期待 } ... イベント用条件変数 cvと一緒に使うmutex イベントを通知する側 イベントを受ける側 ここで通知を待つ 問題その2 spurious wakeup 3 1 3 で処理Aが完了していない 2 3 Sprious wakeupとリオーダーは無関係 次ページで訂正
6.
2015/10/4 6 std::condition_variable cv; std::mutex
m; Item39 ... { std::unique_lock<std::mutex> lk(m); cv.wait(lk); } ... イベント用条件変数 cvと一緒に使うmutex イベントを通知する側 イベントを受ける側 ここで通知を待つ 問題その2 spurious wakeup 1 1 でnotifyされていないのに、ブロックが解除される。 http://d.hatena.ne.jp/yohhoy/20120326/p1 参照
7.
2015/10/4 7 Item39 ... { std::unique_lock<std::mutex> lk(m); cv.wait(lk); ... } cv.wait(lk, []{
return whether the event has occurred; }); イベントを受ける側 ラムダ式を引数に取るversionのwaitを使う 本当にイベントが発生したかどうか確認する。 発生していたらtrue このアプローチの詳細は後述 その前に
8.
2015/10/4 8 Item39 flag =
true; フラグを用いたポーリングベースのアプローチ std::atomic<bool> flag(false); atomic変数 while(!flag); イベント通知 イベントを受けての処理 ?mutex不要 ?whileループの前にフラグがセットされても問題ない ?spurious wakeupの問題も無い ?ループが回り続けるため、CPUリソースを消費する そこで、条件変数とフラグベースアプローチを組み合わせる イベントを受ける側 イベントを通知する側
9.
2015/10/4 9 Item39 ... { std::unique_lock<std::mutex> lk(m); flag
= true; } cv.notify_one(); { std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{ return flag; }); ... } ... イベントを通知する側 ロックされた区間でフラグを設定 std::condition_variable cv; std::mutex m; bool flag(false); flag操作はmutexでロックされた区間で行われるので atomicでなくてよい 条件変数でイベント通知 イベントを受ける側 本当にイベントが発生したかどうか確認する。 発生していたらtrue 問題は全て解決する がコードはそこそこ複雑 この例ではflagはglobal よってキャプチャ不要
10.
2015/10/4 10 Item39 p.set_value(); std::promise<void> p;
voidのpromiseを準備 p.get_future().wait(); イベント通知 イベントを受けての処理 future/promiseを使ったアプローチ イベントを待つ
11.
2015/10/4 11 Item39 実際にスレッドを使ったコード std::promise<void> p; void
react(); void detect() { std::thread t([] { p.get_future().wait(); react(); }); ... p.set_value(); ... t.join(); } イベントを待つ側のスレッド イベントを送る側のスレッド イベントを受けての処理 tをunjoinableに Item37参照 この例ではpはglobal よってキャプチャ不要
12.
2015/10/4 12 Item39 future/promiseを使ったアプローチ std::promise<void> p; void
react(); void detect() { ThreadRAII tr( std::thread([] { p.get_future().wait(); react(); }), ThreadRAII::DtorAction::join ); ... p.set_value(); ... } Item37のThreadRAIIを使えばシンプルに?
13.
2015/10/4 13 Item39 future/promiseを使ったアプローチ std::promise<void> p; void
react(); void detect() { ThreadRAII tr( std::thread([] { p.get_future().wait(); react(); }), ThreadRAII::DtorAction::join ); ... p.set_value(); ... } Item37のThreadRAIIを使えばシンプルに? ここで例外が発生したら
14.
2015/10/4 14 Item39 future/promiseを使ったアプローチ std::promise<void> p; void
react(); void detect() { ThreadRAII tr( std::thread([] { p.get_future().wait(); react(); }), ThreadRAII::DtorAction::join ); ... p.set_value(); ... } Item37のThreadRAIIを使えばシンプルに? ここで例外が発生したら スレッドは待ったまま set_value()は呼ばれない
15.
2015/10/4 15 Item39 future/promiseを使ったアプローチ std::promise<void> p; void
react(); void detect() { ThreadRAII tr( std::thread([] { p.get_future().wait(); react(); }), ThreadRAII::DtorAction::join ); ... p.set_value(); ... } Item37のThreadRAIIを使えばシンプルに? ここで例外が発生したら スレッドは待ったまま set_value()は呼ばれない ThreadRAIIのデストラクタでjoinするため、そこでブロックし、 ThreadRAIIのデストラクタが永久に終わらない
16.
2015/10/4 16 Item39 future/promiseを使ったアプローチ std::promise<void> p; void
react(); void detect() { ThreadRAII tr( std::thread([] { p.get_future().wait(); react(); }), ThreadRAII::DtorAction::join ); { scope_exit se([] {p.set_value();}); ... } ... } struct scope_exit { scope_exit(std::function<void (void)> f) : f_(std::move(f)) {} ~scope_exit(void) { f_(); } private: std::function<void (void)> f_; }; http://melpon.org/wandbox/permlink/NdFo1yH7d9t0O2FL
17.
2015/10/4 17 Item39 future/promiseを使ったアプローチ(スレッドが複数の場合) std::promise<void> p; void
detect() { auto sf = p.get_future().share(); std::vector<std::thread> vt; for (int i = 0; i < threadsToRun; ++i) { vt.emplace_back([sf]{ sf.wait(); react(); }); } ... p.set_value(); ... for (auto& t : vt) { t.join(); } } std::shared_futureを使う 例外を検知して p.set_value()する処理は必要
18.
2015/10/4 18 Item39 ? シンプルなイベント通知を条件変数を用いて実現する場合、 mutexとイベントが発生したかのチェックが必要になる。 ?
この問題を回避するためにフラグを導入すれば良いが、 ポーリングベースだとブロックしない ? 条件変数とフラグを組み合わせは、 通知メカニズムを堅苦しいもの(stilted)にする ? std::promiseとfutureの組み合わせでこの問題を解決できる が、shared stateのためのheap memoryを必要とし、また、一 度きりの通知にしか使えない Things to Remember
Download