我必须使用atomic <bool>作为"exit"bool变量吗?

Pow*_*mer 30 c++ atomic c++11

我需要为另一个线程设置一个标志来退出.另一个线程会不时检查退出标志.我是否必须使用原子作为旗帜或只是一个普通的布尔就足够了(为什么(如果我使用普通布尔可能会出错的例子)?

#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();
}
Run Code Online (Sandbox Code Playgroud)

And*_*owl 29

我是否必须使用atomic来"退出"bool变量?

是的.

atomic<bool>通过(例如)使用或使用手动同步std::mutex.您的程序当前包含数据争用,其中一个线程可能正在读取变量而另一个线程正在编写它.这是未定义的行为.

根据C++ 11标准的第1.10/21段:

程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会在另一个之前发生.任何此类数据争用都会导致 未定义的行为.

" 冲突 " 的定义见第1.10/4段:

如果其中一个修改内存位置(1.7)而另一个访问或修改相同的内存位置,则两个表达式评估会发生冲突.

  • 为了详细说明(标准),标准中的数据争用条件不仅仅是关于另一个线程可见的一个线程可以执行的操作.它还涉及优化或代码重新排序.编译器可以假设没有数据争用,因此它可以假设如果一个线程读取变量但不修改它,则值不能在同步点之间改变.您的主题可能永远不会退出 或者,如果线程在不使用它的情况下设置标志,则可以重新排序修改以在任何其他代码之前(在最后的同步点之后).我甚至忽略了缓存一致性.这就是原子存在的原因. (17认同)
  • 在实践中,我几乎看不出如何访问“bool”,而不是原子性的(尽管我同意在正式的标准语言中,这完全是另一回事)。无论如何,除了竞争条件本身之外,我相信还需要一个内存屏障(由“atomic&lt;&gt;”或“std::mutex”提供)来确保编译器不会缓存数据或重新排序指令。然而,我缺乏正确解释这一点的理论知识(尽管我知道如何在实践中使用它),如果可以的话,请您启发我?或者我应该创建一个新问题? (2认同)
  • @PowerGamer:这个"我正在做我想要的,反对正常的建议,除非证明错误"的方法是你应该尝试打破的习惯...... (2认同)

Pet*_*ker 12

是的,你必须有一些同步.正如你所说,最简单的方法是atomic<bool>.

正如@AndyProwl所说的那样,语言定义说在这里不使用原子会给出未定义的行为.这有很好的理由.

首先,可以通过线程切换中途中断对变量的读取或写入; 另一个线程可能会看到部分写入的值,或者如果它修改了值,原始线程将看到一个混合值.其次,当两个线程在不同的核心上运行时,它们具有单独的缓存; 写入值会将其存储在缓存中,但不会更新其他缓存,因此线程可能看不到其他线程写入的值.第三,编译器可以根据它看到的内容重新组织代码; 在示例代码中,如果循环内部没有任何内容更改值exit,编译器没有任何理由怀疑该值会发生变化; 它可以将循环转换为while(1).

原子学解决了所有这三个问题.