[英]Is it safe to use a non-atomic boolean without a mutex for control flow across threads?
There are a number of related questions, but this question is arguably more specific. 有许多相关的问题,但这个问题可以说更具体。
Consider the following sample program, note that the bool
is just an ordinary bool
. 考虑以下示例程序,请注意
bool
只是一个普通的bool
。
When I run it, it achieves the desired effect: after the user presses Enter
, hello world
will cease to print. 当我运行它时,它达到了预期的效果:用户按
Enter
, hello world
将停止打印。
If I require only that bar2()
eventually starts returning immediately after run
is to false
, is the logic below guaranteed to be safe by the standard? 如果我只要求
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 world\n");
fflush(stdout);
}
void bar() {
while (true) {
bar2();
sleep(1);
}
}
int main(){
std::thread(bar).detach();
getchar();
run = false;
// Prevent main from exiting.
getchar();
}
There is no guarantee by the standard that without synchronization or ordering guarantees given by eg std::atomic
the other thread will ever see the write to run
. 标准不保证没有同步或由
std::atomic
给出的排序保证,其他线程将看到写入run
。
As a matter of fact the compiler would be perfectly fine to only check once that write
is true and since the thread itself never writes to it cache the value without ever reloading it. 事实上,编译器完全可以只检查一次
write
是否为真,并且因为线程本身从不写入它而不会重新加载它来缓存值。 Now practically speaking with all the c library calls going on the compiler generally cannot know that none of functions writes to run
and under x86 you don't have to worry about not seeing updates from other processors to memory, so it will in practice work (and even under other architectures a context switch would probably resolve the problem). 现在几乎可以说,编译器上的所有c库调用通常都不知道没有函数写入
run
而在x86下你不必担心没有看到从其他处理器到内存的更新,所以它在实际工作中(甚至在其他架构下,上下文切换可能会解决问题)。
But if you're talking purely from the standard's point of view? 但是,如果你纯粹从标准的角度谈论? No guarantees whatsoever.
没有任何保证。
It is certainly the case that the standard makes no guarantees of how this code will behave. 当然,标准不保证此代码的行为方式。 The testing and setting of the
run
variable are not properly sequenced. run
变量的测试和设置未正确排序。 As a consequence, relying on the order of setting and testing constitutes undefined behaviour. 因此,依赖于设置和测试的顺序构成了未定义的行为。
The question of what a "real world" compiler will actually do is less easy. “真实世界”编译器实际上会做什么的问题不那么容易。 There are several things a compiler could reasonably do which would cause this program to fail.
编译器可以合理地做一些会导致该程序失败的事情。 It could:
它可能:
if(!run)
test from the program, since without the UB it can never have any effect. if(!run)
测试,因为没有UB它永远不会有任何影响。 run
variable is never tested after it was set, and delete the run=false
, since it can never have any effect. run
变量在设置后从未进行过测试,并删除run=false
,因为它永远不会产生任何影响。 [This may involve assuming no UB.] If it was my choice, I wouldn't rely on this code. 如果这是我的选择,我不会依赖这个代码。 I would use the capabilities provided by the standard and write conforming behaviour.
我会使用标准提供的功能并编写符合规范的行为。
yes, it is completely safe. 是的,这是完全安全的。 most processors have 32 bit pipelines.
大多数处理器都有32位流水线。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.