有一个使用循环正常工作的线程
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;
}
}现在,这在调试器上运行得很好。但在优化代码中,它被困在shutdown函数的while(1)循环中。为什么?
注意:我可能应该用互斥锁来锁定共享变量。
发布于 2010-12-14 00:55:13
因为编译器没有意识到shutdown_ok将在不同线程中的函数外部被修改。也许编译器发现在Shutdown()函数中shutdown_ok的计算结果总是为false,因此删除了if语句。
void Shutdown (void)
{
shutdown = 1;
while (1)
{
shutdown = 0;
}
}可以将变量标记为volatile,这是对编译器的一个提示,即可以用编译器无法预测的方式修改变量。
C++标准7.1.5.1/8:注意:
volatile是对实现的一个提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测到的方式更改...一般而言,C++中volatile的语义与C中的语义相同。
但是,编译器可能会使volatile变量具有标准中未指定的某些行为。例如,Visual C++ compilers make volatile variables behave like memory locks,但实际上任何标准都不能保证这种行为。
因此,volatile不能被视为解决所有多线程问题的灵丹妙药。对于这项工作,最好使用适当的线程和并发原语。
发布于 2010-12-14 01:35:39
事情没有按照你期望的方式运行的最可能的原因是编译器并不期望关闭会发生变化,所以它很乐意优化它。解决方案是使用适当的线程同步原语,如信号量或condvars,以获得您期望的行为。
备注
人们会建议将shutdown设为volatile,或许可以安全地假设写入int是原子的。这可能在大多数情况下都有帮助。但即便如此,你仍然可能会遇到问题。在多核机器上,读写操作可能会意外地重新排序,从而导致您错过了一个重要的信号。英特尔有专门的LOCK指令来处理这些情况,但编译器通常不会生成锁定。在ARM上,您有一条DMB指令,但这也不太可能由编译器生成。
底线当做线程同步时,使用操作系统原语,不要尝试推出你自己的。你会弄错的
发布于 2010-12-14 06:31:09
pthreads的解决方案是使用条件变量:
pthread_cond_t shutdown_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t shutdown_lock = PTHREAD_MUTEX_INITIALIZER;
void* Thread (void* nothing) {
while(1) {
// Sleep if requested
if ( Sleep_c)
Sys_Sleep (Sleep_c);
Do_stuff();
pthread_mutex_lock(&shutdown_lock);
if (shutdown) {
shutdown_ok = 1;
pthread_cond_signal(&shutdown_cond);
pthread_mutex_unlock(&shutdown_lock);
break;
}
pthread_mutex_unlock(&shutdown_lock);
}
return 0;
}
void Shutdown (void)
{
pthread_mutex_lock(&shutdown_lock);
shutdown = 1;
while (!shutdown_ok)
pthread_cond_wait(&shutdown_cond, &shutdown_lock);
shutdown = 0;
pthread_mutex_unlock(&shutdown_lock);
}通常,如果您发现自己想要进行繁忙循环,这是您应该使用条件变量的标志。
https://stackoverflow.com/questions/4431244
复制相似问题