In the simplest possible example, let's say I have a function that starts a thread, which in turn sets the value of a local variable to true. We join the thread, then leave the function.
bool func() {
bool b = false;
std::thread t([&]() { b = true; });
t.join();
return b;
}
Will this function return true, or is the behavior undefined?
Yes, it must return true.
[thread.thread.member]
void join();
4 Effects: Blocks until the thread represented by
*this
has completed.5 Synchronization: The completion of the thread represented by
*this
synchronizes with ([intro.multithread]) the corresponding successfuljoin()
return.
So the execution of the thread represented by the handle, and associated side effects are done before join
returns to the calling context.
Let's look at two functions, which differ only in when they join a thread:
int count_A() {
int counter = 0;
bool flag(true);
auto t = std::thread([&]{flag = false;});
while(flag) { // infinite loop - flag never synchronized
++counter;
}
t.join(); // joins thread after loop exits
return counter;
}
int count_B() {
int counter = 0;
bool flag(true);
auto t = std::thread([&]{flag = false;});
t.join(); // joins thread before loop, forcing synchronization
while(flag) {
++counter;
}
return counter;
}
When compiled with g++ version 8.2 at -O3
optimization, invoking count_A
results in an infinite loop because the compiler assumes flag
is always true.
On the other hand, invoking count_B
will just return a value of 0
. Because the value of flag
is checked after thread.join()
, it's value is re-loaded, and flag is false
so the while loop doesn't execute.
Note that if flag
is changed to an atomic_bool
, then count_A
has the intended behavior of incrementing the counter until the flag is set to false, and the function does not enter an infinite loop (instead returning once flag
is set to false by the child thread).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With