有许多相关的问题,但这个问题可以说是更具体的。
考虑下面的示例程序,注意bool
只是一个普通的bool
。
运行后,达到了预期效果:用户按下Enter
后,hello world
停止打印。
如果我只要求bar2()
最终在run
到false
之后立即开始返回,那么下面的逻辑是否保证是安全的?
#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
变量的测试和设置顺序不正确。因此,依赖于设置和测试的顺序构成了未定义的行为。
"真实世界"的编译器实际上会做什么这个问题就不那么容易了。编译器可能做的一些事情会导致程序失败。它可以:
- 检测UB并建立自己的特定于实现的操作过程,这可能是您想要的,也可能不是。至少如果你测试它,你会发现哪一个。
- 假设UB没有发生,并从程序中删除
if(!run)
测试,因为没有UB它永远不会有任何效果。 - 观察
run
变量在设置后从未测试过,并删除run=false
,因为它永远不会产生任何影响。[这可能涉及假设没有UB]
如果是我的选择,我不会依赖这段代码。我将使用标准提供的功能并编写符合标准的行为。
是的,这是完全安全的。大多数处理器都有32位管道。