我正在努力理解顺序一致排序对负载意味着什么。考虑一下这个人为的例子:
#include <atomic>
#include <thread>
#include <cassert>
static std::atomic<bool> preStop {false};
static std::atomic<bool> stop {false};
static std::atomic<int> counter{0};
void waiter() {
preStop.store(true, std::memory_order_relaxed);
while (counter.load() > 0);
stop.store(true, std::memory_order_relaxed);
}
void performer() {
while (true) {
counter.fetch_add(1);
const bool visiblePreStop = preStop.load();
if (stop.load()) {
assert(visiblePreStop);
return;
}
counter.fetch_sub(1);
std::this_thread::yield();
}
}
int main() {
std::thread performerThread(performer);
std::thread waiterThread(waiter);
waiterThread.join();
performerThread.join();
}
assert
会失败吗?或counter.fetch_add()
与counter.load()
同步吗?
我的理解是,如果counter
上的操作具有std::memory_order_relaxed
或std::memory_order_acq_rel
,则负载-负载对不会创建同步点。std::memory_order_seq_cst
对负载-负载对有什么不同吗?
断言可能失败。counter.load()
的顺序一致性意味着它被获取,但这并不妨碍松弛的preStop.store(true)
在它之后被重新排序。然后preStop.store(true)
也可以在stop.store(true)
之后被重新排列。如果我们有一个带有存储缓冲区的弱序机器,那么waiter()
中的任何东西都不会耗尽存储缓冲区,因此preStop.store()
可能会在那里滞留任意长的时间。
如果是这样,那么代码完全有可能做类似的事情
waiter
======
preStop.store(true, relaxed); // suppose not globally visible for a while
while (counter.load() > 0); // terminates immediately
stop.store(true, relaxed); // suppose globally visible right away
performer
=========
counter.fetch_add(1);
preStop.load(); // == false
stop.load(); // == true
不过,我不太理解这个问题的其余部分。同步是由发布存储和读取存储值(或发布序列中稍后的另一个值(的获取负载建立的;当这种情况发生时,它证明存储发生在加载之前。两个负载不能相互同步,即使它们顺序一致也是如此。
但counter.fetch_add(1)
是一个读-修改-写;它由一个加载和一个存储组成。由于是seq_cst
,所以获取负载并释放存储。并且counter.load()
也是获取的,所以如果它返回1,它确实与counter.fetch_add(1)
同步,证明counter.fetch_add(1)
发生在counter.load()
之前。
但这并没有真正的帮助。问题是waiter
根本不做任何释放存储,所以performer
中的任何东西都不能与之同步。因此,waiter
中的松弛存储都不能被证明发生在performer
中相应的加载之前,因此我们不能保证任何一个加载都会返回true
。特别地,preStop.load()
返回false
并且stop.load()
返回true
是非常可能的。
您有一个问题,您正在读取两个不同的原子变量,但希望它们的状态是相互依赖的。这与检查时间到使用时间错误类似,但不相同。
以下是断言触发的有效交错:
- 创建了PerformerThread(PT(
- WaiterThread(WT(已创建
- PT执行以下操作:
while (true) {
counter.fetch_add(1);
const bool visiblePreStop = preStop.load();
- PT发现
visiblePreStop
是false
- PT暂停
- WT执行以下操作:
preStop.store(true, std::memory_order_relaxed);
while (counter.load() > 0);
stop.store(true, std::memory_order_relaxed);
- WT已暂停
- PT执行以下操作:
if (stop.load()) {
- PT发现
stop
是true
- PT命中断言,因为
visiblePreStop
为false,而stop
为"true">