在最简单的示例中,假设我有一个启动线程的函数,该函数依次将局部变量的值设置为true。我们加入线程,然后离开函数。
bool func() {
bool b = false;
std::thread t([&]() { b = true; });
t.join();
return b;
}
此函数将返回true,还是行为未定义?
答案 0 :(得分:6)
是的,它必须返回true。
[thread.thread.member]
void join();
4 效果:阻塞直到由
*this
表示的线程完成。5 同步:由
*this
表示的线程的完成与([intro.multithread])相应的成功join()
返回同步。>
因此,在join
返回调用上下文之前,要完成由句柄表示的线程的执行以及相关的副作用。
让我们看一下两个函数,它们的区别仅在于它们加入线程的时间不同:
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;
}
在以-O3
优化的g ++版本8.2进行编译时,调用count_A
将导致无限循环,因为编译器假定flag
始终为true。
另一方面,调用count_B
只会返回值0
。由于flag
的值是在thread.join()
之后检查的,因此将其值重新装入,并且标志为false
,因此while循环不会执行。
请注意,如果将flag
更改为atomic_bool
,则count_A
具有增加计数器的预期行为,直到标志设置为false,并且函数不是进入无限循环(相反,一旦子线程将flag
设置为false,则返回)。