c++マルチスレッドの異常環境での待機

3893 ワード

c++11はマルチスレッドプログラミングをサポートし始め、関連するクラスと関数が標準ライブラリヘッダファイルにカプセル化されているが、c++マルチスレッドプログラミングが重要なのは、ユーザーがstd::threadオブジェクトを作成し、呼び出し可能なオブジェクトを関連付けた後、threadオブジェクトが破棄される前にjoin()またはdetach()を呼び出す必要があることである.ここでjoin()関数は、スレッドを結合するために使用され、threadオブジェクトを作成する関数が終了する前にスレッドが実行されることを確認します.detach()関数は、分離スレッドを表します.すなわち、スレッドをバックグラウンドで実行し、スレッドの所有権と制御権をc++ランタイムライブラリに渡します.threadオブジェクトが破棄される前にjoin()またはdetach()が呼び出されない場合、プログラムは終了します(threadの構造関数呼び出しstd::terminate()).
ユーザがスレッドを分離する場合は、通常、スレッドが起動した直後にdetach()を呼び出せばよい.しかし、スレッドを待つ必要がある場合は、join()を呼び出すタイミングが重要です.スレッドが開始後、join()を呼び出す前に異常が発生した場合、join()の呼び出しはスキップされます.例外環境でのスレッドの待機を保証する方法の1つは、try/catch文ブロックを使用することです.
 1 #include <thread>
 2 #include <stdexcept>
 3 
 4 using std::thread;
 5 using std::runtime_error;
 6 
 7 void func();
 8 
 9 void TestThread() {
10     thread thrd(func);
11     try
12     {
13         //...
14     }
15     catch (runtime_error re)
16     {
17         thrd.join();
18         throw re;
19     }
20     thrd.join();
21 }

しかしtry/catch文ブロックの使用は煩雑であり,役割ドメインを混乱させやすいことは明らかである.より良い方法は、リソース取得・初期化(Resource Acquisition Is Initialization,RAII)を使用する方法であり、RAIDはC++のリソース管理、漏洩回避の慣用である.C++標準は、構築されたオブジェクトが最終的に破棄されることを保証します.すなわち、その構造関数が最終的に呼び出されます.簡単に言えば、RAIIのやり方は1つのオブジェクトを使用して、その構造の時に資源を獲得して、オブジェクトの生命期に資源に対するアクセスを制御して終始有効にして、最後にオブジェクトが構造を分析する時に資源を解放します.
RAIDを使用するコードは次のとおりです.
 1 #include <thread>
 2 
 3 using std::thread;
 4 
 5 void func();
 6 
 7 class Thread_RAII {
 8 private:
 9     thread &thrd;
10 public:
11     explicit Thread_RAII(thread &p_thrd) : thrd(p_thrd) {}
12     ~Thread_RAII()
13     {
14         if (thrd.joinable())
15         {
16             thrd.join();
17         }
18     }
19     Thread_RAII(const Thread_RAII &) = delete;
20     Thread_RAII & operator=(const Thread_RAII &) = delete;
21 };
22 
23 void TestThread()
24 {
25     thread thrd(func);
26     Thread_RAII tr(thrd);
27     //...
28 }

関数TestThread()が最後まで実行されると、ローカルオブジェクトは構造の逆順序で破棄されるため、trは最初に破棄され、その中のthrdオブジェクトが結合され、前の文の実行時に異常が発生して終了した場合でも同様である.
上記のコードには、次の2つの点に注意する必要があります.
1構造関数はjoin()を呼び出す前にthrdがjoinable()であるかどうかを確認する必要があります.なぜなら、1つのスレッドに対してjoin()は1回しか呼び出せないからです.
      ②Thread_RAIIクラスのコピーコンストラクタおよびコピーコピー演算子は、threadオブジェクトとunique_のため削除されます.ptrは同様にコピーできない(ただし移動可能).