首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么编译器优化会破坏这种功能?(涉及的线程)

为什么编译器优化会破坏这种功能?(涉及的线程)
EN

Stack Overflow用户
提问于 2010-12-14 00:52:32
回答 5查看 190关注 0票数 0

有一个使用循环正常工作的线程

代码语言:javascript
运行
复制
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;
}

主线程上可能会杀死它的函数

代码语言:javascript
运行
复制
 void Shutdown (void) {

    shutdown = 1;
    while (1) // Wait for it
        if (shutdown_ok) {
            shutdown = 0;
            break;
        }
 }

现在,这在调试器上运行得很好。但在优化代码中,它被困在shutdown函数的while(1)循环中。为什么?

注意:我可能应该用互斥锁来锁定共享变量。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-12-14 00:55:13

因为编译器没有意识到shutdown_ok将在不同线程中的函数外部被修改。也许编译器发现在Shutdown()函数中shutdown_ok的计算结果总是为false,因此删除了if语句。

代码语言:javascript
运行
复制
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不能被视为解决所有多线程问题的灵丹妙药。对于这项工作,最好使用适当的线程和并发原语。

票数 4
EN

Stack Overflow用户

发布于 2010-12-14 01:35:39

事情没有按照你期望的方式运行的最可能的原因是编译器并不期望关闭会发生变化,所以它很乐意优化它。解决方案是使用适当的线程同步原语,如信号量或condvars,以获得您期望的行为。

备注

人们会建议将shutdown设为volatile,或许可以安全地假设写入int是原子的。这可能在大多数情况下都有帮助。但即便如此,你仍然可能会遇到问题。在多核机器上,读写操作可能会意外地重新排序,从而导致您错过了一个重要的信号。英特尔有专门的LOCK指令来处理这些情况,但编译器通常不会生成锁定。在ARM上,您有一条DMB指令,但这也不太可能由编译器生成。

底线当做线程同步时,使用操作系统原语,不要尝试推出你自己的。你会弄错的

票数 3
EN

Stack Overflow用户

发布于 2010-12-14 06:31:09

pthreads的解决方案是使用条件变量:

代码语言:javascript
运行
复制
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);
}

通常,如果您发现自己想要进行繁忙循环,这是您应该使用条件变量的标志。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4431244

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档