Асинхронная функция, дающая противоречивые результаты
У меня есть функция, которая работает асинхронно. К сожалению, он только иногда выплевывает правильный ответ. Значения представлены futures[i].get()
меняйте каждый раз, когда я запускаю код. Я новичок в многопоточности.
double async_func() const {
vector<future<double>> futures;
double val = 0;
for (int i = 0; i < rows; i++) {
futures.push_back(std::async(std::launch::async, [&] {return minor(i,0).determinant();}) );
}
for (int i = 0; i < rows; i++)
val += futures[i].get();
return val;
}
1 ответ
Проблема заключается в захвате по ссылке.
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)
перед петлей.