I'm trying to understand Multi-threading in c++, but I'am stuck in this problem: if I launch threads in a for loop they print wrong values. This is the code:
#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;
}
I was expecting to get printed the values 0,1,2,3,4 but I often got the same value twice. This is the output:
Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5
What I'm missing?
The
[&]
syntax is causingi
to be captured by reference. So quite often thereforei
will be further advanced when the thread runs than you might expect. More seriously, the behaviour of your code is undefined ifi
goes out of scope before a thread runs.Capturing
i
by value - i.e.std::thread([i](){ print_id(i); })
is the fix.Another thing :
Do not wait until to have always an ordered sequence: 0, 1, 2, 3, ... because the multithreading execution mode has a specificity: indeterminism.
Indeterminism means that the execution of the same program, under the same conditions, gives a different result.
This is due to the fact that the OS schedules threads differently from one execution to another depending on several parameters: CPU load, priority of other processes, possible system interruptions, ...
Your example contains only 5 threads, so it's simple, try to increase the number of threads, and for example put a sleep in the processing function, you will see that the result can be different from one execution to another .
Two problems:
You have no control over when the thread runs, which means the value of the variable
i
in the lambda might not be what you expect.The variable
i
is local for the loop and the loop only. If the loop finishes before one or more thread runs, those threads will have an invalid reference to a variable whose lifetime have ended.You can solve both these problems very simply by capturing the variable
i
by value instead of by reference. That means each thread will have a copy of the value, and that copy will be made uniquely for each thread.