在线程间的控制流中使用不带互斥锁的非原子布尔值是否安全?



有许多相关的问题,但这个问题可以说是更具体的。

考虑下面的示例程序,注意bool只是一个普通的bool

运行后,达到了预期效果:用户按下Enter后,hello world停止打印。

如果我只要求bar2() 最终runfalse之后立即开始返回,那么下面的逻辑是否保证是安全的?

#include <thread>
#include <mutex>
#include <unistd.h>

bool run = true;
void bar2() {
    // Is this check safe without a lock?
    if (!run) return;
    printf("hello worldn");
    fflush(stdout);
}
void bar() {
    while (true) {
        bar2();
        sleep(1);
    }
}
int main(){
    std::thread(bar).detach();
    getchar();
    run = false;
    // Prevent main from exiting.
    getchar();
}

如果没有std::atomic提供的同步或排序保证,其他线程将无法看到对run的写入。

事实上,编译器只检查一次write是否为真,并且由于线程本身从未写入它缓存值而从未重新加载它,因此编译器将非常好。现在实际上,所有的c库调用都在进行,编译器通常不知道没有一个函数写入run,在x86下,你不必担心看不到其他处理器对内存的更新,所以它在实践中是可行的(甚至在其他架构下,上下文切换可能会解决这个问题)。

但是如果你纯粹从标准的角度来看呢?没有任何保证。

当然,标准不保证这些代码将如何表现。run变量的测试和设置顺序不正确。因此,依赖于设置和测试的顺序构成了未定义的行为。

"真实世界"的编译器实际上会做什么这个问题就不那么容易了。编译器可能做的一些事情会导致程序失败。它可以:

  1. 检测UB并建立自己的特定于实现的操作过程,这可能是您想要的,也可能不是。至少如果你测试它,你会发现哪一个。
  2. 假设UB没有发生,并从程序中删除if(!run)测试,因为没有UB它永远不会有任何效果。
  3. 观察run变量在设置后从未测试过,并删除run=false,因为它永远不会产生任何影响。[这可能涉及假设没有UB]

如果是我的选择,我不会依赖这段代码。我将使用标准提供的功能并编写符合标准的行为。

是的,这是完全安全的。大多数处理器都有32位管道。

最新更新