为什么编译器优化会破坏此功能? (涉及线程)
有一个线程可以在循环中正常工作
void* Thread (void* nothing) {
while(1) {
// Sleep if requested
if ( Sleep_c)
Sys_Sleep (Sleep_c);
Do_stuff();
if (shutdown) {
shutdown_ok = 1;
break;
}
}
return 0;
}
主线程上的函数可能会杀死它
void Shutdown (void) {
shutdown = 1;
while (1) // Wait for it
if (shutdown_ok) {
shutdown = 0;
break;
}
}
现在,这在调试器上工作得很好。但在优化代码中,它陷入了 [关闭函数] 的 while(1) 循环中。为什么?
注意:我可能应该互斥锁共享变量。
There's a thread that works normally with a loop
void* Thread (void* nothing) {
while(1) {
// Sleep if requested
if ( Sleep_c)
Sys_Sleep (Sleep_c);
Do_stuff();
if (shutdown) {
shutdown_ok = 1;
break;
}
}
return 0;
}
The function on the main thread that may kill it does
void Shutdown (void) {
shutdown = 1;
while (1) // Wait for it
if (shutdown_ok) {
shutdown = 0;
break;
}
}
Now, this works fine on the debugger. But it gets stuck in the while(1) loop [of the shutdown function] in optimized code. Why?
Note: I should probably mutex lock the shared variables.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
因为编译器没有意识到
shutdown_ok
会在不同线程的函数外部被修改。也许编译器发现shutdown_ok
在Shutdown()
函数中始终会计算为 false,因此删除了if
语句。人们可以将变量标记为
易失性
,这向编译器提示该变量可以以编译器无法预测的方式进行修改。但是,编译器可能会使
>易失性
变量具有标准中未指定的某些行为。例如,Visual C++ 编译器使易失性
变量的行为类似于内存锁,但这种行为实际上并没有任何标准保证。因此,
volatile
不能被视为解决所有多线程问题的灵丹妙药。对于这项工作,使用适当的线程和并发原语会更好。Because the compiler does not realize that
shutdown_ok
will be modified outside the function in a different thread. Perhaps the compiler figured out thatshutdown_ok
will always evaluate to false inside theShutdown()
function and thus removed theif
statement.One can mark a variable as
volatile
, which serves as a hint to the compiler that the variable can be modified in ways the compiler cannot predict.However, compilers may make
volatile
variables take on certain behaviors that are not specified in the standard. For example, Visual C++ compilers makevolatile
variables behave like memory locks, but such behavior is not actually guaranteed by any standard.For this reason,
volatile
cannot be treated as a magic bullet that will solve all your multithreading problems. You're much better off using proper threading and concurrency primitives for this job.事情没有按照您期望的方式运行的最可能原因是编译器不希望关闭发生变化,因此很乐意对其进行优化。解决方案是使用适当的线程同步原语(例如信号量或条件变量)来获得您期望的行为。
注意
人们会建议将 shutdown 设置为 易失性 ,并且可以安全地假设写入 int 是原子的。这在大多数情况下可能会有所帮助。但即便如此,您仍然可能会遇到问题。在多核机器上,读取和写入可能会意外地重新排序,从而导致您错过重要信号。 Intel 有专门的 LOCK 指令来处理这些情况,但编译器通常不会生成 LOCK。在 ARM 上,您有一条 DMB 指令,但这也不太可能由编译器生成。
进行线程同步时的底线是使用操作系统原语,不要尝试推出自己的原语。你会弄错的
The most likely reason for things not behaving the way that you expect is that the compiler is not expecting shutdown to change so it is happy to optimize it out. The solution is to use proper thread synchronization primitives like semaphores or condvars to get the behaviour that you are expecting.
Note
People will suggest making shutdown
volatile
and it is probably safe to assume writing to an int is atomic. This may help in probably most cases. But even so you may still get problems. On multicore machines reads and writes may be reordered unexpectedly leaving you to missing an important signal. Intel has a specialized LOCK instruction to deal with these cases but the compiler will not normally generate a LOCK. On ARM you have a DMB instruction but this as well is unlikely to be generated by the compiler.Bottom line when doing thread synchronization, use the OS primitive and don't try to roll out your own. YOU WILL GET IT WRONG
pthreads 的解决方案是使用条件变量:
一般来说,如果您发现自己想要忙循环,则表明您应该使用条件变量。
The pthreads solution to this is to use a condition variable:
In general, if you find yourself wanting to busy-loop, it's a sign that you should be using a condition variable.
shutdown的解释是什么?它在哪里定义的?我怀疑这需要是一个 易失性 变量。
What is the definition of shutdown? Where is it defined? I suspect that this needs to be a volatile variable.
我认为“shutdown_ok”的“易失性”修饰符应该避免这种情况。它告诉编译器该变量可以由另一个线程修改,以便它应该始终引用它,而不是使用例如寄存器来保存其值的副本。
I think 'volatile' modifier for 'shutdown_ok' should avoid this. It tells the compiler that the variable can be modified by another thread so that it should always refer to it instead of using for example a register to hold a copy of its value.