I broke down my problem to something that is reproducible. In the following code we have a class printer
. printer::run
will print a the id_
of that printer
. printer::spawn
will get you a thread with a new printer.
In main
I am spawning 10 printers. The created threads will be joined at the end of main
. My expectation would have been that we get the numbers from 0 to 9 in some order every time we run this (this is the "deterministic behavior" from the title). This is not the case. We get other numbers sometimes. Why is that?
#include <thread>
#include <iostream>
#include <vector>
class printer
{
public:
printer(int i) : i_(i){};
void run()
{
std::cout << i_ << '\n';
}
std::shared_ptr<std::thread> spawn()
{
return std::make_shared<std::thread>([=]() { run(); });
}
private:
int i_;
};
int main()
{
std::vector<std::shared_ptr<std::thread>> vec;
for (int i = 0; i < 10; ++i)
{
printer p{i};
vec.push_back(p.spawn());
}
for (auto a : vec)
{
a->join();
}
return 0;
}
Some thoughts about my expectation:
I would have guessed that we copy the state of the current printer
when calling spawn, because we capture the state by value (lambda), not by reference.
Even if we would capture by reference, we still copy the state to the new thread.
And even if that is not happening, each thread has its own printer
. What am I missing?
Some example outputs:
(I put all the numbers in one line for brevity)
1, 3, 4, 2, 5, 6, 7, 8, 9, -116618192
1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144, 1513168144
1, 2, 33, , 6, 6, 7, 9, 9, 1344437296
1, 43, 2, 6, , 6, 7, 8, 9, -1194894256
Some outputs do not have all 10 numbers...
This is because you create and destroy a printer object within the loop that creates your threads. When the loop enters the next iteration, the printer created in the previous iteration has been destroyed, and the thread that you created to reference it will reference this destroyed object. This is Undefined Behavior.
Essentially all your thread will be using the same printer
object. At some point the memory it occupies gets overwritten when it is used to hold other data.
You'll probably want to create an array of printers, so that you can pass a different printer to each thread.