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

发布于 2024-10-07 12:51:38 字数 602 浏览 0 评论 0原文

有一个线程可以在循环中正常工作

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

开始看清了 2024-10-14 12:51:38

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

void Shutdown (void)
{        
    shutdown = 1;
    while (1)
    {
        shutdown = 0;
    }
}

人们可以将变量标记为易失性,这向编译器提示该变量可以以编译器无法预测的方式进行修改。

C++ 标准 7.1.5.1/8: [注意: 易失性 是对实现的提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测到的方式进行更改...一般来说,易失性 的语义在 C++ 中应与在 C 中相同。]

但是,编译器可能会使 >易失性 变量具有标准中未指定的某些行为。例如,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 that shutdown_ok will always evaluate to false inside the Shutdown() function and thus removed the if statement.

void Shutdown (void)
{        
    shutdown = 1;
    while (1)
    {
        shutdown = 0;
    }
}

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.

C++ standard 7.1.5.1/8: [Note: volatile is a hint to the implementation to avoid aggressive optimization involve the object because the value of the object might be changed by means undetectable by an implementation... In general, the semantics of volatile are intended to be the same in C++ as they are in C.]

However, compilers may make volatile variables take on certain behaviors that are not specified in the standard. For example, Visual C++ compilers make volatile 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.

时光暖心i 2024-10-14 12:51:38

事情没有按照您期望的方式运行的最可能原因是编译器不希望关闭发生变化,因此很乐意对其进行优化。解决方案是使用适当的线程同步原语(例如信号量或条件变量)来获得您期望的行为。

注意

人们会建议将 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

墟烟 2024-10-14 12:51:38

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);
}

一般来说,如果您发现自己想要忙循环,则表明您应该使用条件变量。

The pthreads solution to this is to use a condition variable:

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);
}

In general, if you find yourself wanting to busy-loop, it's a sign that you should be using a condition variable.

ま昔日黯然 2024-10-14 12:51:38

shutdown的解释是什么?它在哪里定义的?我怀疑这需要是一个 易失性 变量。

What is the definition of shutdown? Where is it defined? I suspect that this needs to be a volatile variable.

一瞬间的火花 2024-10-14 12:51:38

我认为“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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文