顺序一致的原子负载(负载-负载对)是否形成线程间同步点

do sequentially-consistent atomic loads (load-load pair) form an inter-thread synchronisation point?

本文关键字:负载 是否 线程 同步 顺序      更新时间:2023-10-16

我正在努力理解顺序一致排序对负载意味着什么。考虑一下这个人为的例子:

#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_relaxedstd::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是非常可能的。

您有一个问题,您正在读取两个不同的原子变量,但希望它们的状态是相互依赖的。这与检查时间到使用时间错误类似,但不相同。

以下是断言触发的有效交错:

  1. 创建了PerformerThread(PT(
  2. WaiterThread(WT(已创建
  3. PT执行以下操作:
while (true) {
counter.fetch_add(1);
const bool visiblePreStop = preStop.load();
  1. PT发现visiblePreStopfalse
  2. PT暂停
  3. WT执行以下操作:
preStop.store(true, std::memory_order_relaxed);
while (counter.load() > 0);
stop.store(true, std::memory_order_relaxed);
  1. WT已暂停
  2. PT执行以下操作:
if (stop.load()) {
  1. PT发现stoptrue
  2. PT命中断言,因为visiblePreStop为false,而stop为"true">