问题在于引用捕获。
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)
在循环之前添加。