effective modern c++ 勉強会#8 item38
TRANSCRIPT
Thread handles
•ここでは std::thread と std::future, std::shared_future のこと
• std::future と std::shared_future はこの資料では合わせて future と呼ぶことにする
3
std::thread destructor
• joinable である場合、std::terminate が呼ばれてプロセス停止
• implicit join/detach を避けた(Item 37 参照)
4
future の destructor
•時には implicit join のように振る舞う
•時には implicit detach のように振る舞う
•時には何もしないように振る舞う
•何故???
5
callee 結果の保存場所
• callee (promise)でも caller (future) 側でもない• callee 側は先に destructor 呼ばれるケースあり
• caller 側は future が移動/共有されるケースあり
• future から参照され、参照カウントで管理される shared state に結果が書かれる
6
future destructor の振る舞い
•条件A: std::async 由来の shared state を参照
•条件B: ポリシが std::launch::async
•条件C: 自分が shared state を参照する最後のfuture
•条件ABCを全て満たす場合、block し、タスクの完了を待つ
•それ以外の場合、単にリソースを開放する
7
何故???
• 理由1: implicit detach は避けたかった(Item 37 参照)
• 理由2: だからといって、std::thread のようにstd::terminate を呼んでほしくなかった(std::async は高級だから??)
• だから必要があれば implicit join することにした
• 色々と是非が議論された(らしい)が、C++11 からC++14 においてはそのまま
8
future destructor の観察
9
int f() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << “task end” << std::endl;
}
int main() {
{
std::packaged_task<int()> pt(f);
auto fut = pt.get_future();
#if 0
std::thread t(std::move(pt));
#elif 0
auto fut1 = std::async(std::launch::deferred, std::move(pt));
#elif 0
auto fut1 = std::async(std::launch::async, std::move(pt));
#endif
}
std::cout << “block end” << std::endl;
}
std::sync の返り値を受けなかった場合
• std::launch::deferred 結果を受けとる手段がないので永遠に実行されない。
• std::launch::async その場で future が destructor が呼ばれ、imlicit join によってタスク完了を待つ
10eratta comments
Item 38 Things to remember
• Future の destructor は通常単にメンバのdestructor を呼んでメモリ開放するだけである
• std::async non-deferred 呼び出し経由のshared state を持つ最後の future destructor はタスク完了を待つ (implicit join())
11