問題は、参照によるキャプチャにあります。
for (int i = 0; i < rows; i++)
{
futures.push_back(std::async(std::launch::async, [&] // capture by reference!
{
return minor(i,0).determinant();
}));
}
実行された各メソッドは の独自の値を取得するのではi
なく、ループ変数 への参照を取得するため、これは問題ですi
。i
したがって、関数呼び出しに対して が評価されるとすぐにminor(i, 0)
、それはすでに変更されている可能性があります。さらに悪いことに、ループの終了前に関数呼び出しがまだ実行されていない可能性があります。したがってi
、使用される前に破壊された可能性があります。
上記の説明では不十分な場合は、このタイムラインが役立つ可能性があります。これは実際の状況ではなく、単なる例であることに注意してください。
i
反復には 2 ステップ、関数のパラメーターの評価には 2 ステップ、スレッドの開始には 5 ステップが必要であると仮定します (実際のマシンでは、おそらくそれ以上です!)。各スレッドによって実行されるジョブには、数千のステップが必要になる場合があります (それが、それらを並列化する理由ですよね?)。
Time -->
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
| | | | | | | | | | | | | | Thread 3 function call.
| | | | | | | | | | | | | Thread 3 evaluate parameters (i == garbage!).
| | | | | | | | | | | | Thread 3 start; Thread 2 function call.
| | | | | | | | | | | Thread 2 evaluate parameters (i == garbage!).
| | | | | | | | | | Destroy i; Thread 2 start; Thread 1 function call.
| | | | | | | | | Loop condition broken; Thread 1 evaluate parameters (i == 4 !).
| | | | | | | | Thread 0 function call; Thread 1 start; i = 4;
| | | | | | | Thread 3 request to start; Thread 0 evaluate parameters (i == 3 !).
| | | | | | Thread 0 start; i = 3;
| | | | | Thread 2 request to start.
| | | | i = 2.
| | | Thread 1 request to start.
| | i = 1.
| Thread 0 request to start.
Create i, i = 0.
したがって、意味のあるデータを取得するスレッドもあれば、取得しないスレッドもあります。i
実際のタイミングによっては、関数パラメーターの値を評価すると同時にインクリメントできる可能性があります。この場合、競合状態が発生します。
i
解決策は、値でキャプチャすることです。
for (int i = 0; i < rows; i++)
{
futures.push_back(std::async(std::launch::async, [=] // capture by value
{
return minor(i,0).determinant();
}));
}
推奨事項: 非同期ビットを扱うときは、キャプチャする内容を明示してください。
for (int i = 0; i < rows; i++)
{
futures.push_back(std::async(std::launch::async, [i] // explicit capture by value
{
return minor(i,0).determinant();
}));
}
また、先物のベクトルへの再割り当てを避ける必要があります。futures.reserve(rows)
ループの前に追加するだけです。