yohhoyの日記

技術的メモをしていきたい日記

threadの利用と例外安全(その3)

C++11標準ライブラリとBoost.Threadライブラリ(Boost 1.48.0)に含まれる、threadオブジェクトのデストラクタの振る舞いと例外安全に関するメモ。その1, その2 の続き。

2020-12-02追記:C++2a(C++20)標準ライブラリでは、デストラクタで自動的にjoinを呼び出すstd::jthreadが追加される。std::thread動作はC++11時点と同一。

2013-02-05追記:Boost.Thread 1.50.0〜1.56.0では記事内容に関する破壊的変更が行われる。id:yohhoy:20120206 も参照のこと。

問題のまとめ

  • ローカル変数の参照/ポインタを新スレッドへ渡した後に、例外送出などで該当関数から抜けると、boost::threadデストラクタにて意図しないスレッドdetachが行われる*1
  • その結果、別スレッドの実行中に渡されたオブジェクト生存期間が終了してしまい、ダングリング参照/ポインタへの書き込み操作によりスタックメモリ領域が破壊される。
  • さらに、メモリ破壊は別スレッドから不定タイミングで行われるため、再現性のない原因特定困難なバグとなる。

この問題に対しC++11標準ライブラリでは、プログラマが明示的にスレッドjoinまたはdetachを行わない限り、std::threadデストラクタはstd::terminateを呼び出してプログラムを終了させる。この動作仕様により、少なくとも問題箇所を早期に発見できる。

対策のまとめ

  • (可能なら)boost::threadではなくstd::threadを使う。
  • 別スレッドにローカル変数の参照/ポインタを渡さない。別スレッドへの引数渡しは値のコピーまたはムーブを用い、別スレッドからの処理結果取得にはfuture*2を用いる。
  • 別スレッドにローカル変数の参照/ポインタを渡す場合は、RAIIイディオムを用いてスレッドjoinが行われることを保証する。


関連URL

*1:特に例外送出に限った話ではなく、処理途中での通常returnでも同じことが起きうる。

*2:C++11標準ライブラリの std::future、またはBoost.Thread の boost::unique_future。