忙于等待atomic<bool>

问题描述 投票:0回答:1

编译器可以在紧密的无限循环中提升对原子变量的检查吗?

std::atomic<bool> ready = false;
void run() {
    while(!ready){}
    // ... do something
}

编译器是否可以自由地将上面的代码重新排列成这样

std::atomic<bool> ready = false;
void run() {
    bool is_ready = ready;
    while(!is_ready){}
    // ... do something
}

https://www.youtube.com/watch?v=F6Ipn7gCOsY&ab_channel=CppCon 17:24,Arthur O'Dwyer 说编译器可以重新排列。这似乎很违反直觉。如果我将调用更改为ready.load(),会阻止编译器重新排列代码吗?如果不是,那 CAS 操作怎么会在 while 循环中进行呢?编译器不能以类似的方式重新排列它们吗?

c++ atomic
1个回答
0
投票

不,亚瑟弄错了。编译器必须假设其他线程在读取之间更改

std::atomic
对象的值,因为并发读 + 写不是数据争用 UB。 (对于非原子变量,数据竞争 UB 允许这种转换编译器在实践中会这样做。)

[简介.进度] / 18:

实现应确保原子或同步操作分配的最后一个值(按修改顺序)将在有限的时间内对所有其他线程可见。

[原子.order] / 11:

推荐实践: 实现应使原子存储对原子加载可见,并且原子加载应在合理的时间内观察原子存储。

这些只是 ISO C++ 标准中的“应该”要求,但这种转换会使实现在实践中毫无用处。

这些要求不强于“应该”的原因是允许在重载操作系统上运行多线程程序,并且调度程序不能保证公平性,例如如果有其他高优先级(或实时优先级)线程占用了所有 CPU 时间,则线程可能会无限期地挨饿。不是因为标准的作者认为编译器静态地决定循环永远不应该看到存储(如果它在第一次迭代之前不可见)是合理的。


另请参阅

如果您使用“memory_order_relaxed”进行检查,为什么要使用“memory_order_seq_cst”设置停止标志?(没有原因,这是 Herb Sutter 演讲中的糟糕建议。)

在您的情况下,在读取值之前旋转等待,您需要

.load(std::memory_order_acquire)

,但编译器仍然无法转换为
.load(relaxed)
的无限循环。

© www.soinside.com 2019 - 2024. All rights reserved.