38

我需要为另一个线程设置一个标志才能退出。那个其他线程不时检查退出标志。我是否必须使用 atomic 作为标志,或者只是一个普通的 bool 就足够了,为什么(举例说明如果我使用普通的 bool 可能会出现什么问题)?

#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();
}
4

3 回答 3

33

我必须对“退出”布尔变量使用原子吗?

的。

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

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

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

第 1.10/4 段给出了“冲突”的定义:

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

于 2013-04-19T18:58:31.863 回答
15

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

正式地,正如@AndyProwl 所说,语言定义说在这里不使用原子会产生未定义的行为。这是有充分理由的。

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

Atomics 解决了所有这三个问题。

于 2013-04-19T19:06:53.560 回答
-3

实际上,在这个特定示例中,纯 bool 没有任何问题。唯一的注意是将 bool 退出变量声明为 volatile 以将其保存在内存中。CISC 和 RISC 架构都将 bool 读/写实现为严格的原子处理器指令。现代多核处理器也具有先进的智能缓存实现。因此,不需要任何内存屏障。标准引用不适用于这种特殊情况,因为它处理唯一的一次写入和唯一一个线程的读取。

于 2015-04-15T08:10:07.287 回答