Я пытаюсь понять многопоточность в c ++, но я застрял в этой проблеме: если я запускаю потоки в цикле for, они печатают неправильные значения. Это код:
#include <iostream>
#include <list>
#include <thread>
void print_id(int id){
printf("Hello from thread %d\n", id);
}
int main() {
int n=5;
std::list<std::thread> threads={};
for(int i=0; i<n; i++ ){
threads.emplace_back(std::thread([&](){ print_id(i); }));
}
for(auto& t: threads){
t.join();
}
return 0;
}
Я ожидал напечатать значения 0,1,2,3,4, но часто получал одно и то же значение дважды. Это вывод:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
Что мне не хватает?
c++
multithreading
Ermando
источник
источник
i
по значению к лямбда,[i]
.emplace_back
странно:emplace_back
принимает список аргументов и передает их конструктору forstd::thread
. Вы передали (rvalue) экземплярstd::thread
, следовательно, создадите поток, а затем переместите этот поток в вектор. Эта операция лучше выражена более распространенным методомpush_back
. Было бы более разумно либо написатьthreads.emplace_back([i](){ print_id(i); });
(построить на месте), либоthreads.push_back(std::thread([i](){ print_id(i); }));
(построить + переместить), что несколько более идиоматично.Ответы:
[&]
Синтаксис вызываетi
быть захвачен посредством ссылки . Поэтому довольно часто, поэтомуi
процесс будет продвигаться дальше, чем вы ожидаете. Более серьезно, поведение вашего кода не определено, еслиi
выходит из области видимости до запуска потока.Захват
i
по значению - т.е.std::thread([i](){ print_id(i); })
это исправление.источник
std::thread([=](){ print_id(i); })
i
с записью основного потока и чтением других потоков.Две проблемы:
Вы не можете контролировать время выполнения потока, что означает, что значение переменной
i
в лямбда-выражении может не соответствовать ожидаемому.Переменная
i
является локальной только для цикла и цикла. Если цикл завершается до запуска одного или нескольких потоков, эти потоки будут иметь недопустимую ссылку на переменную, время жизни которой закончилось.Вы можете решить обе эти проблемы очень просто, захватив переменную
i
по значению, а не по ссылке. Это означает, что у каждого потока будет копия значения, и эта копия будет сделана уникально для каждого потока.источник
Другое дело:
не ждите, пока всегда будет упорядоченная последовательность: 0, 1, 2, 3, ..., потому что режим многопоточного исполнения имеет специфичность: индетерминизм .
Индетерминизм означает, что выполнение одной и той же программы при одинаковых условиях дает другой результат.
Это связано с тем, что ОС по-разному планирует потоки от одного исполнения к другому в зависимости от нескольких параметров: загрузка процессора, приоритет других процессов, возможные сбои системы, ...
Ваш пример содержит только 5 потоков, так что все просто, попробуйте увеличить количество потоков и, например, поместив режим ожидания в функцию обработки, вы увидите, что результат может отличаться от одного выполнения к другому.
источник