我需要为另一个线程设置一个标志才能退出。另一个线程会不时检查退出标志。我是否必须使用原子作为旗帜,或者仅仅是一个普通的布尔就足够了(为什么(如果我使用普通布尔可能会出错的例子)?
#include <future>
bool exit = false;
void thread_fn()
{
while(!exit)
{
//do stuff
if(exit) break;
//do stuff
}
}
int main()
{
auto f = std::async(std::launch::async, thread_fn);
//do stuff
exit = true;
f.get();
}
答案 0 :(得分:26)
我是否必须使用atomic来“退出”bool变量?
是即可。
使用atomic<bool>
,或通过(例如)std::mutex
使用手动同步。您的程序当前包含数据竞争,其中一个线程可能正在读取变量而另一个线程正在编写它。这是未定义的行为。
根据C ++ 11标准的第1.10 / 21段:
如果程序的执行在不同的线程中包含两个冲突的操作,则会执行数据竞争, 其中至少有一个不是原子的,也不会发生在另一个之前。任何此类数据竞赛都会产生 未定义的行为。
“冲突”的定义见第1.10 / 4段:
两个表达式评估冲突如果其中一个修改了内存位置(1.7)而另一个修改了内存位置 访问或修改相同的内存位置。
答案 1 :(得分:12)
是的,您必须进行一些同步。正如您所说,最简单的方法是使用atomic<bool>
。
正如@AndyProwl所说的那样,语言定义说在这里不使用原子会给出未定义的行为。这有很好的理由。
首先,通过线程切换可以中断对变量的读取或写入;另一个线程可能会看到部分写入的值,或者如果它修改了值,原始线程将看到一个混合值。其次,当两个线程在不同的核心上运行时,它们具有单独的缓存;写入值会将其存储在缓存中,但不会更新其他缓存,因此线程可能看不到其他线程写入的值。第三,编译器可以根据它看到的内容重新组织代码;在示例代码中,如果循环内没有任何内容改变exit
的值,编译器没有任何理由怀疑该值会改变;它可以将循环转换为while(1)
。
Atomics解决了所有这三个问题。
答案 2 :(得分:-4)
实际上,在这个特定的例子中,普通布尔没有出错。唯一的通知是将bool exit变量声明为volatile以将其保存在内存中。 CISC和RISC架构都将bool读/写实现为严格的原子处理器指令。现代多核处理器也具有先进的智能缓存实现。所以,任何记忆障碍都是不必要的。标准引用不适用于这种特殊情况,因为它只处理一个写作和唯一一个帖子的读取。