SlideShare una empresa de Scribd logo
1 de 19
Effective Modern C++
勉強会 #8 Item 37
内田 公太 (@uchan_nos)
サイボウズ株式会社
2015/08/19
アジェンダ
• Item 37: Make std::threads unjoinable on all
paths.
std::threads をすべてのパスで unjoinable にしよう
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
• 「joinable な std::thread」は裏にあるスレッド
が実行中か実行可能な状態になっているスレッドに対
応する
• ブロック中かスケジュールを待っている状態のスレッド
• まさに実行中のスレッド
• 「unjoinable な std::thread」は joinable でな
いスレッドのこと
すべての std::thread は
joinable / unjoinable のどちらかの状態
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
• デフォルトコンストラクタで生成した std::thread
• 何も実行するものがなく裏のスレッドに紐づかない
• 他のオブジェクトに move 済みの std::thread
• move の結果、裏のスレッドが他の std::thread に移る
• join 済みの std::thread
• join すると裏のスレッドも実行を停止し、切り離される
• detach 済みの std::thread
• detach すると裏のスレッドとの接続が切れる
unjoinable になるパターン
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
なぜ join 可能かどうかが重要なのか
→ joinable な std::thread がデストラクトされると
プログラムの実行が終了してしまうから。
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
constexpr auto tenMillion = 10000000;
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
});
if (conditionsAreSatisfied()) {
t.join();
performComputation(goodVals);
return true;
}
return false;
}
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
constexpr auto tenMillion = 10000000;
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
});
if (conditionsAreSatisfied()) {
t.join();
performComputation(goodVals);
return true;
}
return false;
}
重い処理
→並列実行
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
constexpr auto tenMillion = 10000000;
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
});
if (conditionsAreSatisfied()) {
t.join();
performComputation(goodVals);
return true;
}
return false;
}
joinable な t がデスト
ラクトされる系がある
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
joinable な std::thread がデストラクトされると
プログラムの実行が終了してしまうから。
→ デストラクタで対策すれば良いのでは?
• 暗黙に join してあげれば良さそう
• しかし追跡しずらい性能上の問題が出るかも。例えば
conditionsAreSatisfied() が既に false を返したのに
フィルタが終わるのを待ち続けるとか。
• 暗黙に detach してあげれば良い?
• 裏で動くスレッドが切り離された後も実行を続けるので、いろい
ろ良くないことが起きる。
• 例えば・・・(次ページ)
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
constexpr auto tenMillion = 10000000;
bool doWork(...)
{
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
});
...
}
ローカル変数
への参照
将来他の関数のスタックフレームが doWork のスタックフレーム
だった領域(goodVals があった領域)まで到達すると、
スタック領域が自然に書き換わるように見える
これをデバッグする楽しさといったら無いよ!
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
joinable な std::thread がデストラクトされると
プログラムの実行が終了してしまうから。
→ デストラクタで対策すれば良いのでは?
• 標準規格では join か detach のどちらも採用せず、
単にプログラムを終了することになった
• 全パスで確実に unjoinable にするために、独自の
RAII クラスを書いて対策しよう!
• std::unique_ptr とか std::shared_ptr とか
std::fstream とか標準にはたくさんの RAII があるけど、
残念ながら std::thread に対する RAII クラスは無い…
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
class ThreadRAII {
public:
enum class DtorAction { join, detach };
ThreadRAII(std::thread&& t, DtorAction a)
: action(a), t(std::move(t)) {}
~ThreadRAII() {
if (t.joinable()) {
if (action == DtorAction::join) {
t.join();
} else {
t.detach();
}
}
}
std::thread& get() { return t; }
private:
DtorAction action;
std::thread t;
};
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
class ThreadRAII {
public:
enum class DtorAction { join, detach };
ThreadRAII(std::thread&& t, DtorAction a)
: action(a), t(std::move(t)) {}
~ThreadRAII() {
if (t.joinable()) {
if (action == DtorAction::join) {
t.join();
} else {
t.detach();
}
}
}
std::thread& get() { return t; }
private:
DtorAction action;
std::thread t;
};
引数の順序は thread が先だが
宣言の順序は thread が後
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
class ThreadRAII {
public:
enum class DtorAction { join, detach };
ThreadRAII(std::thread&& t, DtorAction a)
: action(a), t(std::move(t)) {}
~ThreadRAII() {
if (t.joinable()) {
if (action == DtorAction::join) {
t.join();
} else {
t.detach();
}
}
}
std::thread& get() { return t; }
private:
DtorAction action;
std::thread t;
};
引数の順序は thread が先だが
宣言の順序は thread が後
thread は初期化されるとすぐ動き
出すので、クラスの一番後ろで宣言
するのは良い習慣。
スレッドオブジェクトが構築された
ときには、他のデータメンバが初期
化されているのを保証できる。
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
if (t.joinable()) {
if (action == DtorAction::join) {
t.join();
} else {
t.detach();
}
}
• joinable() の検査は必要
• unjoinable スレッドに対して join/detach は未定義動作
• 検査と join/detach の間に隙間があるので競合?
• メンバ関数呼び出しを通してのみ unjoinable になれる
• → ThreadRAII がデストラクトされるときには、他のスレッ
ドはメンバ関数を呼び出せないはずである。
マルチスレッドで
競合しそう…
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::vector<int> goodVals;
std::thread t([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
});
auth nh = t.native_handle();
...
if (conditionsAreSatisfied()) {
t.join();
performComputation(goodVals);
return true;
}
return false;
}
最初のコード
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
bool doWork(std::function<bool(int)> filter,
int maxVal = tenMillion)
{
std::vector<int> goodVals;
ThreadRAII t(
std::thread([&filter, maxVal, &goodVals]
{
for (auto i = 0; i <= maxVal; ++i)
{ if (filter(i)) goodVals.push_back(i); }
}),
ThreadRAII::DtorAction::join
);
auth nh = t.get().native_handle();
...
if (conditionsAreSatisfied()) {
t.get().join();
performComputation(goodVals);
return true;
}
return false;
}
ThreadRAII を使ったコード
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
class ThreadRAII {
public:
enum class DtorAction { join, detach };
ThreadRAII(std::thread&& t, DtorAction a)
: action(a), t(std::move(t)) {}
~ThreadRAII() {
…
}
ThreadRAII(ThreadRAII&&) = default;
ThreadRAII& oprator=(ThreadRAII&&) = default;
std::thread& get() { return t; }
private:
DtorAction action;
std::thread t;
};
• ユーザ定義デストラクタがあるので、moveコンストラ
クタが自動生成されなくなる。
• moveできなくする理由はないので作っておきましょう。
• std::thread をすべての系で unjoinable にする
• 「破棄時に join 戦略」はデバッグしにくい性能上の
問題の原因となる
• 「破棄時に detach 戦略」はデバッグしにくい未定義
動作を引き起こす
• std::thread はデータメンバの最後で宣言しよう
覚えておくべきこと
Item 37: Make std::threads unjoinable on all paths.
std::threads をすべてのパスで unjoinable にしよう
2015年8月28日出版予定

Más contenido relacionado

La actualidad más candente

Effective Modern C++ 勉強会 Item26
Effective Modern C++ 勉強会 Item26Effective Modern C++ 勉強会 Item26
Effective Modern C++ 勉強会 Item26
Akihiro Nishimura
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Genya Murakami
 

La actualidad más candente (20)

Effective Modern C++ Item 9 and 10
Effective Modern C++ Item 9 and 10Effective Modern C++ Item 9 and 10
Effective Modern C++ Item 9 and 10
 
Effective Modern C++ 勉強会#3 Item16
Effective Modern C++ 勉強会#3 Item16Effective Modern C++ 勉強会#3 Item16
Effective Modern C++ 勉強会#3 Item16
 
Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27
 
Boost Fusion Library
Boost Fusion LibraryBoost Fusion Library
Boost Fusion Library
 
templateとautoの型推論
templateとautoの型推論templateとautoの型推論
templateとautoの型推論
 
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
Boost.勉強会 #21 札幌「C++1zにstring_viewが導入されてうれしいので紹介します」
 
Emcpp item31
Emcpp item31Emcpp item31
Emcpp item31
 
Effective Modern C++ 勉強会 Item26
Effective Modern C++ 勉強会 Item26Effective Modern C++ 勉強会 Item26
Effective Modern C++ 勉強会 Item26
 
二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ
二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ
二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
Effective Modern C++ 勉強会 Item 22
Effective Modern C++ 勉強会 Item 22Effective Modern C++ 勉強会 Item 22
Effective Modern C++ 勉強会 Item 22
 
ret2dl resolve
ret2dl resolveret2dl resolve
ret2dl resolve
 
関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml関数型プログラミング入門 with OCaml
関数型プログラミング入門 with OCaml
 
すごいConstたのしく使おう!
すごいConstたのしく使おう!すごいConstたのしく使おう!
すごいConstたのしく使おう!
 
詳説WebAssembly
詳説WebAssembly詳説WebAssembly
詳説WebAssembly
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
C++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISるC++コミュニティーの中心でC++をDISる
C++コミュニティーの中心でC++をDISる
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 

Similar a Effective modern c++ 8 (7)

第9回ACRiウェビナー_日立/島田様ご講演資料
第9回ACRiウェビナー_日立/島田様ご講演資料第9回ACRiウェビナー_日立/島田様ご講演資料
第9回ACRiウェビナー_日立/島田様ご講演資料
 
Flutterを体験してみませんか
Flutterを体験してみませんかFlutterを体験してみませんか
Flutterを体験してみませんか
 
Web技術勉強会 第38回
Web技術勉強会 第38回Web技術勉強会 第38回
Web技術勉強会 第38回
 
大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方
大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方
大江戸Ruby会議01 "mission critical"なシステムでも使えるThreadの作り方
 
規格書で読むC++11のスレッド
規格書で読むC++11のスレッド規格書で読むC++11のスレッド
規格書で読むC++11のスレッド
 
C++11のつかいかた
C++11のつかいかたC++11のつかいかた
C++11のつかいかた
 
10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!10のJava9で変わるJava8の嫌なとこ!
10のJava9で変わるJava8の嫌なとこ!
 

Más de uchan_nos

Más de uchan_nos (20)

MikanOSと自作CPUをUSBで接続する
MikanOSと自作CPUをUSBで接続するMikanOSと自作CPUをUSBで接続する
MikanOSと自作CPUをUSBで接続する
 
OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事
 
小型安価なFPGAボードの紹介と任意波形発生器
小型安価なFPGAボードの紹介と任意波形発生器小型安価なFPGAボードの紹介と任意波形発生器
小型安価なFPGAボードの紹介と任意波形発生器
 
トランジスタ回路:エミッタ接地増幅回路
トランジスタ回路:エミッタ接地増幅回路トランジスタ回路:エミッタ接地増幅回路
トランジスタ回路:エミッタ接地増幅回路
 
OpeLa: セルフホストなOSと言語処理系を作るプロジェクト
OpeLa: セルフホストなOSと言語処理系を作るプロジェクトOpeLa: セルフホストなOSと言語処理系を作るプロジェクト
OpeLa: セルフホストなOSと言語処理系を作るプロジェクト
 
自作言語でお絵描き
自作言語でお絵描き自作言語でお絵描き
自作言語でお絵描き
 
OpeLa 進捗報告 at 第23回自作OSもくもく会
OpeLa 進捗報告 at 第23回自作OSもくもく会OpeLa 進捗報告 at 第23回自作OSもくもく会
OpeLa 進捗報告 at 第23回自作OSもくもく会
 
サイボウズ・ラボへ転籍して1年を振り返る
サイボウズ・ラボへ転籍して1年を振り返るサイボウズ・ラボへ転籍して1年を振り返る
サイボウズ・ラボへ転籍して1年を振り返る
 
USB3.0ドライバ開発の道
USB3.0ドライバ開発の道USB3.0ドライバ開発の道
USB3.0ドライバ開発の道
 
Security Nextcamp remote mob programming
Security Nextcamp remote mob programmingSecurity Nextcamp remote mob programming
Security Nextcamp remote mob programming
 
Langsmith OpeLa handmade self-hosted OS and LPS
Langsmith OpeLa handmade self-hosted OS and LPSLangsmith OpeLa handmade self-hosted OS and LPS
Langsmith OpeLa handmade self-hosted OS and LPS
 
OpeLa セルフホストなOSと言語処理系の自作
OpeLa セルフホストなOSと言語処理系の自作OpeLa セルフホストなOSと言語処理系の自作
OpeLa セルフホストなOSと言語処理系の自作
 
自動でバグを見つける!プログラム解析と動的バイナリ計装
自動でバグを見つける!プログラム解析と動的バイナリ計装自動でバグを見つける!プログラム解析と動的バイナリ計装
自動でバグを見つける!プログラム解析と動的バイナリ計装
 
1を書いても0が読める!?隠れた重要命令INVLPG
1を書いても0が読める!?隠れた重要命令INVLPG1を書いても0が読める!?隠れた重要命令INVLPG
1を書いても0が読める!?隠れた重要命令INVLPG
 
レガシーフリーOSに必要な要素技術 legacy free os
レガシーフリーOSに必要な要素技術 legacy free osレガシーフリーOSに必要な要素技術 legacy free os
レガシーフリーOSに必要な要素技術 legacy free os
 
Building libc++ for toy OS
Building libc++ for toy OSBuilding libc++ for toy OS
Building libc++ for toy OS
 
プランクトンサミットの歴史2019
プランクトンサミットの歴史2019プランクトンサミットの歴史2019
プランクトンサミットの歴史2019
 
Introduction of security camp 2019
Introduction of security camp 2019Introduction of security camp 2019
Introduction of security camp 2019
 
30分で分かる!OSの作り方 ver.2
30分で分かる!OSの作り方 ver.230分で分かる!OSの作り方 ver.2
30分で分かる!OSの作り方 ver.2
 
Timers
TimersTimers
Timers
 

Effective modern c++ 8

  • 1. Effective Modern C++ 勉強会 #8 Item 37 内田 公太 (@uchan_nos) サイボウズ株式会社 2015/08/19
  • 2. アジェンダ • Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう
  • 3. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう • 「joinable な std::thread」は裏にあるスレッド が実行中か実行可能な状態になっているスレッドに対 応する • ブロック中かスケジュールを待っている状態のスレッド • まさに実行中のスレッド • 「unjoinable な std::thread」は joinable でな いスレッドのこと すべての std::thread は joinable / unjoinable のどちらかの状態
  • 4. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう • デフォルトコンストラクタで生成した std::thread • 何も実行するものがなく裏のスレッドに紐づかない • 他のオブジェクトに move 済みの std::thread • move の結果、裏のスレッドが他の std::thread に移る • join 済みの std::thread • join すると裏のスレッドも実行を停止し、切り離される • detach 済みの std::thread • detach すると裏のスレッドとの接続が切れる unjoinable になるパターン
  • 5. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう なぜ join 可能かどうかが重要なのか → joinable な std::thread がデストラクトされると プログラムの実行が終了してしまうから。
  • 6. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう constexpr auto tenMillion = 10000000; bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); if (conditionsAreSatisfied()) { t.join(); performComputation(goodVals); return true; } return false; }
  • 7. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう constexpr auto tenMillion = 10000000; bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); if (conditionsAreSatisfied()) { t.join(); performComputation(goodVals); return true; } return false; } 重い処理 →並列実行
  • 8. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう constexpr auto tenMillion = 10000000; bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); if (conditionsAreSatisfied()) { t.join(); performComputation(goodVals); return true; } return false; } joinable な t がデスト ラクトされる系がある
  • 9. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう joinable な std::thread がデストラクトされると プログラムの実行が終了してしまうから。 → デストラクタで対策すれば良いのでは? • 暗黙に join してあげれば良さそう • しかし追跡しずらい性能上の問題が出るかも。例えば conditionsAreSatisfied() が既に false を返したのに フィルタが終わるのを待ち続けるとか。 • 暗黙に detach してあげれば良い? • 裏で動くスレッドが切り離された後も実行を続けるので、いろい ろ良くないことが起きる。 • 例えば・・・(次ページ)
  • 10. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう constexpr auto tenMillion = 10000000; bool doWork(...) { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); ... } ローカル変数 への参照 将来他の関数のスタックフレームが doWork のスタックフレーム だった領域(goodVals があった領域)まで到達すると、 スタック領域が自然に書き換わるように見える これをデバッグする楽しさといったら無いよ!
  • 11. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう joinable な std::thread がデストラクトされると プログラムの実行が終了してしまうから。 → デストラクタで対策すれば良いのでは? • 標準規格では join か detach のどちらも採用せず、 単にプログラムを終了することになった • 全パスで確実に unjoinable にするために、独自の RAII クラスを書いて対策しよう! • std::unique_ptr とか std::shared_ptr とか std::fstream とか標準にはたくさんの RAII があるけど、 残念ながら std::thread に対する RAII クラスは無い…
  • 12. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう class ThreadRAII { public: enum class DtorAction { join, detach }; ThreadRAII(std::thread&& t, DtorAction a) : action(a), t(std::move(t)) {} ~ThreadRAII() { if (t.joinable()) { if (action == DtorAction::join) { t.join(); } else { t.detach(); } } } std::thread& get() { return t; } private: DtorAction action; std::thread t; };
  • 13. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう class ThreadRAII { public: enum class DtorAction { join, detach }; ThreadRAII(std::thread&& t, DtorAction a) : action(a), t(std::move(t)) {} ~ThreadRAII() { if (t.joinable()) { if (action == DtorAction::join) { t.join(); } else { t.detach(); } } } std::thread& get() { return t; } private: DtorAction action; std::thread t; }; 引数の順序は thread が先だが 宣言の順序は thread が後
  • 14. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう class ThreadRAII { public: enum class DtorAction { join, detach }; ThreadRAII(std::thread&& t, DtorAction a) : action(a), t(std::move(t)) {} ~ThreadRAII() { if (t.joinable()) { if (action == DtorAction::join) { t.join(); } else { t.detach(); } } } std::thread& get() { return t; } private: DtorAction action; std::thread t; }; 引数の順序は thread が先だが 宣言の順序は thread が後 thread は初期化されるとすぐ動き 出すので、クラスの一番後ろで宣言 するのは良い習慣。 スレッドオブジェクトが構築された ときには、他のデータメンバが初期 化されているのを保証できる。
  • 15. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう if (t.joinable()) { if (action == DtorAction::join) { t.join(); } else { t.detach(); } } • joinable() の検査は必要 • unjoinable スレッドに対して join/detach は未定義動作 • 検査と join/detach の間に隙間があるので競合? • メンバ関数呼び出しを通してのみ unjoinable になれる • → ThreadRAII がデストラクトされるときには、他のスレッ ドはメンバ関数を呼び出せないはずである。 マルチスレッドで 競合しそう…
  • 16. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; std::thread t([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }); auth nh = t.native_handle(); ... if (conditionsAreSatisfied()) { t.join(); performComputation(goodVals); return true; } return false; } 最初のコード
  • 17. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう bool doWork(std::function<bool(int)> filter, int maxVal = tenMillion) { std::vector<int> goodVals; ThreadRAII t( std::thread([&filter, maxVal, &goodVals] { for (auto i = 0; i <= maxVal; ++i) { if (filter(i)) goodVals.push_back(i); } }), ThreadRAII::DtorAction::join ); auth nh = t.get().native_handle(); ... if (conditionsAreSatisfied()) { t.get().join(); performComputation(goodVals); return true; } return false; } ThreadRAII を使ったコード
  • 18. Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう class ThreadRAII { public: enum class DtorAction { join, detach }; ThreadRAII(std::thread&& t, DtorAction a) : action(a), t(std::move(t)) {} ~ThreadRAII() { … } ThreadRAII(ThreadRAII&&) = default; ThreadRAII& oprator=(ThreadRAII&&) = default; std::thread& get() { return t; } private: DtorAction action; std::thread t; }; • ユーザ定義デストラクタがあるので、moveコンストラ クタが自動生成されなくなる。 • moveできなくする理由はないので作っておきましょう。
  • 19. • std::thread をすべての系で unjoinable にする • 「破棄時に join 戦略」はデバッグしにくい性能上の 問題の原因となる • 「破棄時に detach 戦略」はデバッグしにくい未定義 動作を引き起こす • std::thread はデータメンバの最後で宣言しよう 覚えておくべきこと Item 37: Make std::threads unjoinable on all paths. std::threads をすべてのパスで unjoinable にしよう 2015年8月28日出版予定