C++のマルチスレッド処理において親スレッドに例外を集約する

結論

こんな感じに書けば良さそうです(C++11以降)。

  1. #include <iostream>
  2. #include <thread>
  3. #include <exception>
  4. #include <functional>
  5. #include <mutex>
  6.  
  7. /* std::threadに渡す関数の例 */
  8. std::mutex cout_mtx;
  9.  
  10. template <typename T>
  11. void add(const T& a, const T& b) {
  12.     std::lock_guard<std::mutex> lock(cout_mtx);
  13.     std::cout << a + b << std::endl;
  14. }
  15.  
  16. void merge_str(const std::string& a, const std::string& b) {
  17.     if(a == "maao" || b == "maao") {
  18.         throw std::invalid_argument("\"maao\" is an invalid word");
  19.     }
  20.     std::lock_guard<std::mutex> lock(cout_mtx);
  21.     std::cout << a + b << std::endl;
  22. }
  23.  
  24. /* 例外を親スレッドに集約する記述の例 */
  25. void run() {
  26.     std::exception_ptr e_ptr;
  27.  
  28.     // std::threadに渡す関数をwrapするlambda式
  29.     // 子スレッドの処理中にcatchされた例外を、
  30.     // 親スレッドのオブジェクトである例外ポインタ(e_ptr)にセットする
  31.     auto gen_th_func = [&](auto f) -> auto {
  32.                            return [&]() -> void {
  33.                                       try { f(); } catch(...) { e_ptr = std::current_exception(); }
  34.                                   };
  35.                        };
  36.  
  37.     // スレッドを起動し、終了を待つ
  38.     auto th1 = std::thread(gen_th_func([&]() { add(1, 1); }));
  39.     auto th2 = std::thread(gen_th_func([&]() { add(0.1, 0.1); }));
  40.     auto th3 = std::thread(gen_th_func([&]() { merge_str("hoge", "huga"); }));
  41.     auto th4 = std::thread(gen_th_func([&]() { merge_str("piyo", "maao"); }));
  42.  
  43.     th1.join();
  44.     th2.join();
  45.     th3.join();
  46.     th4.join();
  47.  
  48.     // 例外ポインタが子スレッド内でthrowされた例外を指していれば、その例外を再送する
  49.     if(e_ptr != nullptr) {
  50.         std::rethrow_exception(e_ptr);
  51.     }
  52. }
  53.  
  54. int main() {
  55.     try {
  56.         run();
  57.     }
  58.     catch(const std::exception& e) {
  59.         std::cout << e.what() << std::endl;
  60.     }
  61. }

実行結果

$ g++ -o test test.cpp -pthread
$ ./test
2
0.2
hogehuga
"maao" is an invalid word

ただし、「ある子スレッドが投げた例外を他の子スレッドが検知する」といったことは出来ません。そのような処理が必要な場合は、フラグをスレッド間の共有リソースとして用意して、上記のlambda式中のcatch文の中で書き換えるなどの処置が必要です。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です