2つのスレッドを実行しているC++ 11プログラムがあり、そのうちの1つが未処理の例外をスローした場合、どうなりますか?プログラム全体が激しい死に方をするでしょうか?例外がスローされたスレッドは単独で停止しますか(その場合、この場合、例外を取得できますか)?他に完全に何か?
実際には何も変わっていません。 n3290の表現は次のとおりです。
一致するハンドラーが見つからない場合、関数
std::terminate()
が呼び出されます
terminate
の動作はset_terminate
でカスタマイズできますが、
必須の動作:
terminate_handler
は、呼び出し元に戻らずにプログラムの実行を終了します。
そのため、このような場合、プログラムは終了し、他のスレッドは実行を継続できません。
例外の伝播には正当な関心があるようで、これは少なくとも多少は問題に関連しているため、ここに私の提案を示します。std::thread
は、構築するのに安全でないプリミティブと見なされます。より高いレベルの抽象化。それらはdoubly例外的に危険です:起動したばかりのスレッド内で例外が発生すると、先に示したようにすべてが爆発します。ただし、std::thread
を起動したスレッドで例外が発生した場合、std::thread
のデストラクタが*this
を結合または分離する必要があるため、問題が発生する可能性があります(または同等にスレッドではない)。これらの要件に違反すると、std::terminate
が呼び出されます。
std::thread
の危険性のコードマップ:
auto run = []
{
// if an exception escapes here std::terminate is called
};
std::thread thread(run);
// notice that we do not detach the thread
// if an exception escapes here std::terminate is called
thread.join();
// end of scope
もちろん、起動するすべてのスレッドを単にdetach
edした場合、その2番目の点で安全であると主張する人もいます。それに関する問題は、いくつかの状況ではjoin
が最も賢明なことです。たとえば、クイックソートの「単純な」並列化では、サブタスクが終了するまで待機する必要があります。これらの状況では、join
は同期プリミティブ(ランデブー)として機能します。
私たちにとって幸運なことに、私が言及したより高いレベルの抽象化は存在し、標準ライブラリに付属しています。それらはstd::async
、std::future
、およびstd::packaged_task
、std::promise
およびstd::exception_ptr
です。上記と同等の例外セーフバージョン:
auto run = []() -> T // T may be void as above
{
// may throw
return /* some T */;
};
auto launched = std::async(run);
// launched has type std::future<T>
// may throw here; nothing bad happens
// expression has type T and may throw
// will throw whatever was originally thrown in run
launched.get();
実際、get
を呼び出したスレッドでasync
を呼び出す代わりに、別のスレッドにbuckを渡すことができます。
// only one call to get allowed per std::future<T> so
// this replaces the previous call to get
auto handle = [](std::future<T> future)
{
// get either the value returned by run
// or the exception it threw
future.get();
};
// std::future is move-only
std::async(handle, std::move(launched));
// we didn't name and use the return of std::async
// because we don't have to