C++ 布尔标志应该始终是原子的吗?

C++ 布尔标志应该始终是原子的吗?,c++,multithreading,c++11,atomic,C++,Multithreading,C++11,Atomic,假设有一个由主线程控制的布尔标志(keep_running)。另一个线程无限期循环,直到该标志变为false int main() { bool keep_running(true); std::thread run( [](bool &keep_running) { while(keep_running) { // do work } }, std::ref(keep_running

假设有一个由主线程控制的布尔标志(keep_running)。另一个线程无限期循环,直到该标志变为false

int main() {
    bool keep_running(true);
    std::thread run( [](bool &keep_running)
    {
        while(keep_running)
        {
            // do work
        }
    }, std::ref(keep_running) );

    // do other work
    keep_running = false;
    run.join();
    return 0;
}
那面旗帜应该是原子的吗

std::atomic<bool> keep_running
被执行。在这种情况下,循环将继续运行一次以上(并非严格需要的)迭代。但就我而言,这是可以接受的

是否存在上述代码可能错误的情况

编辑:


出于性能原因(以及没有bug),我对这一点最感兴趣。因此,在循环中使用std::atomic作为标志会对性能产生负面影响吗?

只是C++11标准禁止(将它们标记为未定义的行为)对非原子变量的并发访问

所以你需要声明这个原子变量

注意,对于读取值使用
keep\u running.load(std::memory\u order\u released)
对于写入值使用
keep\u running.store(true,std::memory\u order\u released)
将消除任何额外的性能开销,因此生成的代码将与没有原子的代码一样快


我想,在非原子版本中可能发生的最坏情况是,标志在当时设置正确


在您的线程中,变量可能会被存储到寄存器中,并且永远不会被重新加载(所以线程永远不会停止)。没有
原子
或其他特殊类型和修饰符,编译器就可以这样做,事实上确实如此。

我会投票,但我认为“可能发生的最坏情况是变量将存储到线程的寄存器中,并且永远不会重新加载”的说法是误导性的。最糟糕的情况可能发生在UB方面。编译器可以做任何事情来响应它,包括在程序中的任何一点出现故障。请加上一个注释,这将是一个很好的答案。这篇文章从UB的概念开始,标准禁止它。我认为没有必要在最后的信息部分重复这一点。但我已经删除了“最坏的”一词,所以这只是可能出现的坏情况之一,而不是唯一的一个。谢谢,这是完美的,我不是说应该重复,只是让OP认为这是唯一可能发生的“最坏的”坏影响是不好的似乎对“将消除任何额外的性能成本”表示+1异议。在非缓存一致性系统上不正确。@Tsyvarev,足够简单-在非一致性系统上,原子写操作,即使是轻松的,也必须传播到所有CPU,而常规写操作除了本地缓存之外,不必去任何地方。当然,在高速缓存相干系统上也是如此。实际上,如果不是非缓存一致系统,实际上没有理由开始使用轻松的内存模型——简单的易失性。在更高级的C++版本中,你会使用<代码>易失性< /代码>。没有它,有可能从一个线程中写入变量,从另一个线程看不到。但是,因为在C++11中有
原子的
,所以您应该使用它来防止同样的问题。当然,它应该是原子的。这为您提供了正确的语义。你为什么还要做其他事情呢?我在想,我既不需要在标志周围设置内存障碍,也不关心指令的重新排序。因此,我认为我可以去掉std::atomic以获得性能。我试图通过编辑问题来澄清这一点。但我没有意识到变量可能会卡在寄存器中,正如Tsyvarev在下面的回答中指出的那样。
while(keep_running)