8
#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&ab_channel=CppCon(最少 17 分钟)

目标是首先打印Hello from A然后允许threadB开始。很明显,应该避免忙等待,因为它会占用大量 CPU。

作者说while (!ready) {}循环可以由编译器优化(通过将值ready放入寄存器),因为编译器看到threadB永远不会休眠,所以ready永远不会改变。但是即使线程从不休眠,另一个线程仍然可以改变值,对吧?没有数据竞争,因为ready它是原子的。作者声明这段代码是UB。有人可以解释为什么允许编译器进行这样的优化吗?

4

1 回答 1

10

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

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

于 2021-08-28T20:48:04.677 回答