質問: {1*0, 2*0, ..., 9*0, 10*1, 11*1, ...} std::for_each( vec.begin(), vec.end(), [](int &v) { std::cout << v << std::endl; }); } ``` 聞きたいこと ------ * std::vector の配列の一部の参照を作る方法について。 * 配列の各要素を(同じ要素にアクセスすることはないとはいえ)別々のスレッドから変更することに問題はないかどうか。 ※ 上記の配列をスレッド数分分割して、スレッドごとに処理するというのは、とりあえず思いついた方法なので、それ以外の方法で高速化可能なら方法はなんでもいいです。 すみませんが、よろしくおねがいします。
回答: > > 配列の各要素を(同じ要素にアクセスすることはないとはいえ)別々のスレッドから変更することに問題はないかどうか。 > > > はい。「`std::vector`の異なる要素に対する別々スレッドからの変更操作」の安全性は、C++標準ライブラリ仕様で保証されます。記事「スレッドセーフという幻想と現実」もご参考にください。 > > std::vector の配列の一部の参照を作る方法について。 > > > C++17現在は「区間の開始/終端位置を表すイテレータ・ペア」として表現するのが妥当かと思います。 C++20からは、`std::vector`のような連続メモリ領域に対して部分区間を表す `std::span`クラステンプレート が導入されます。本質的にはイテレータペアと等価ですが、“区間”を表す単一オブジェクトとして扱えるメリットはあります。 質問中のコード実装では、★箇所でダングリング参照による並行動作バグを引き起こします。変数`begin`, `end`は「forループの各反復処理中でのみ有効なイテレータオブジェクト」を指しますが、★では“参照”として別スレッドへと渡しています。つまり新しいスレッドが処理を始めるタイミングでは、既に該当イテレータオブジェクトが破棄されている **可能性** があり、意図しない不定動作やメモリ破壊などの不具合を引き起こします。 ``` for (int i = 0; i < num_threads; ++i) { auto begin = vec.begin() + i * 10; auto end = begin + 10; threads.push_back(std::thread(&SomeClass::process, &obj, std::ref(begin), std::ref(end), // ★ i)); } ``` スレッド間をまたいで値(このケースではイテレータオブジェクト)を受け渡す場合、単に値を“コピー”するか、オブジェクトの有効範囲を注意深く制御した上で参照渡しを行なってください。 > > 現在は以下のように順番に各要素に処理を行っていますが、1つの処理の計算量が重く、独立して行える処理のため、マルチスレッドで行いたいと思いました。 > > > C++17標準ライブラリ範囲内であれば、並列アルゴリズムが提供されます。ただし、ご利用中の各コンパイラ/ライブラリが対応済みかは別途ご確認ください。 ``` #include #include std::for_each( std::execution::par, // スレッド並列処理を指示 std::begin(vec), std::begin(end), & { // 1要素に対する処理 }); ``` 外部OSSライブラリの利用でもよければ、Intel TBBもオススメです。ライセンスもPermissiveです。