当前位置: 首页 > 知识库问答 >
问题:

为什么允许编译器优化这个繁忙的等待循环?

王旺
2023-03-14
#include <iostream>
#include <thread>
#include <mutex>

int main()
{
    std::atomic<bool> ready = false;

    std::thread threadB = std::thread([&]() {
        while (!ready) {}

        printf("Hello from B\n");
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));

    printf("Hello from A\n");

    ready = true;

    threadB.join();

    printf("Hello again from A\n");
}

这是CppCon谈话中的一个例子https://www.youtube.com/watch?v=F6Ipn7gCOsY

目标是首先从A打印Hello,然后允许线程B启动。很明显,应该避免繁忙等待,因为它占用大量CPU。

作者说,while (!ready) {} 循环可以由编译器进行优化(通过将 ready 的值放入寄存器中),因为编译器看到 threadB 从不Hibernate,因此就绪永远不会被更改。但是,即使线程从不Hibernate,另一个线程仍然可以更改该值,对吗?没有数据竞赛,因为 ready 是原子的。作者指出此代码是 UB。有人可以解释为什么编译器可以进行这样的优化吗?

共有1个答案

马德厚
2023-03-14

作者在视频下方的一条评论中承认自己错了:

我本来是这么想的,但看来我错了。编译器无法将原子读出循环。@17:54 中的建议仍然是正确的 — 您仍然应该非常小心,并注意编译器可能会重新排序、合并或消除原子访问的情况 – 但这种特殊的 while-loop 实际上并不是这种情况。有关编译器如何优化原子访问模式的一些(主要是理论上的)示例,请参阅JF Bastien的N4455“没有理智的编译器会优化原子”http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4455.html

 类似资料:
  • 这与我对“流减少不兼容类型”的回答有关。我不知道为什么我的建议有效,霍尔格正确地敦促我这一点。但即使是他似乎也没有一个明确的解释为什么它能起作用。所以,让我们把它作为自己的问题来问: 以下代码不能在中编译(下面指向ideone的链接是,请参阅http://ideone.com/faq): 这样做是正确的:或者将这个流中的两个谓词结合在一起,就像写: IdeOne演示 尽管事实上我想尝试这一点,但我

  • 今天我遇到了一些我不太理解的复制构造函数。 考虑下一个代码: 然后在将原点分配给复制时调用复制构造函数,这是有意义的。但是,如果我将复制的声明更改为 它不叫。即使当我使用< code>create()函数时,它也不会调用复制构造函数。但是,当将其更改为 它确实调用了复制构造函数。一些研究解释说,允许编译器优化复制构造函数,这听起来是件好事。直到复制构造函数是非默认的,因为那时它可能会,也可能不会做

  • 有问题的代码如下: 编译器会优化它吗?根据C-Standard(如果我理解正确的话),第二个操作数必须提升为;因此乘法必须使用FPU(或fp仿真)完成。 从理论上讲,该操作可以在正常的硬件寄存器中完成,只需添加一个即时的(并且可能是溢出检查)。是否允许编译器执行此优化?是否有已知的编译器这样做?如果是这样,他们是否也会识别该表达式 这是避免有关隐式转换的静态代码检查器警告所必需的? 补充一下:我知

  • 为了更好地理解编译器,特别是汇编语言,我一直在实验一段简单的代码,其中计算第一个数字的总和,这应该导致或. 如代码所示,有两个功能: 在第一个函数中,I从O循环到N,即

  • 问题内容: 这段代码使我凝视了几分钟: (这里的第137行) 我以前从未见过,而且我也不知道Java有一个“ loop”关键字(NetBeans甚至没有像关键字一样给它上色),并且它在JDK 6中可以很好地编译。 有什么解释? 问题答案: 它不是一个keyword,而是一个label。 用法: