与 C++ 原子内存栅栏同步

Synchronization with C++ atomic memory fence

我对使用内存栅栏同步下面的代码有疑问。

std::atomic<int> a = 0;
std::atomic<int> b = 0;

void increase_b() {
  std::atomic_thread_fence(std::memory_order_release);
  b.store(1, std::memory_ordered_relaxed);
}

bool diff() {
  int val_a = a.load(std::memory_ordered_relaxed);
  int val_b = b.load(std::memory_ordered_relaxed);
  return val_b > val_a;
}

void f1() {
  increase_b();
  std::atomic_thread_fence(std::memory_order_seq_cst);
}

void f2() {
  std::atomic_thread_fence(std::memory_order_seq_cst);
  bool result = diff();
}

int main() {
  std::thread t1(f1);
  std::thread t2(f2);
  t1.join(); t2.join();
}

假设 t1 已经完成 f1 然后 t2 刚刚开始 f2,将 t2 看到 b 增加了吗?

您的代码过于复杂。 a=0 永远不会改变,所以它 总是 读作 0。你还不如只有 atomic<int> b=0; 并且只有一个负载 return b.load.

Assume t1 has finished f1 and then t2 just started f2, will t2 see b incremented?

除非您将 t1.join() 置于 std::thread t2(f2); 构造之前,否则您无法检测到时间是这样计算的。这将要求线程 2 中的所有内容都在线程 1 中的所有内容之后排序。(我认为即使 f1 末尾没有 seq_cst 栅栏,但这也无妨。我认为 thread.join 确保在 thread.join)

之后线程内完成的所有内容都是可见的

但是,是的,这种排序可能是偶然发生的,然后当然会起作用。

不能保证在 C++ 术语中这甚至是一个有意义的条件。

但对于大多数(所有?)实际实施来说,这是有可能发生的事情。 thread_fence(mo_seq_cst) 将编译为一个完整的屏障,该屏障会阻塞该线程,直到存储提交(对所有线程全局可见)。因此执行不能离开 f1,直到其他线程的读取可以看到 b 的更新值。 (C++ 标准根据创建同步关系来定义排序和栅栏,而不是根据编译到刷新存储缓冲区的完整屏障来定义。该标准没有提到存储缓冲区或 StoreLoad 重新排序或任何 CPU记忆顺序的东西。)

鉴于综合条件,线程实际上是按顺序排列的。就像所有事情都在一个线程中完成一样。


diff() 中的负载未按顺序排列。彼此,因为他们都是 mo_relaxed。但是 a 永远不会被任何线程修改,所以唯一的问题是 b.load() 是否可以在线程启动之前发生,在 f1 存储可见之前。在实际实现中,它不能因为“然后 t2 刚刚开始 f2”的意思。 如果它可以加载旧值,那么你就不能说“然后”,所以这几乎是同义反复。

负载前的 thread_fence(seq_cst) 没有任何帮助。我猜它会阻止 b.load() 使用线程启动机制重新排序。