原子线程栅栏:为什么在这个非原子变量上存在数据竞争?这有关系吗?

atomic thread fence: Why is there a data race on this non atomic variable? And does it matter?

假设我们有 2 个线程。一个生产者和一个消费者。我们有生产数据的生产者,以及使用这些数据的消费者。然而守卫不是原子的!

bool isDataReady = false;
int data = 0;

void Producer() {
  data = 42;
  std::atomic_thread_fence(std::memory_order_release);
  isDataReady = true;
}

void Consumer() {
  while(!isDataReady);
  std::atomic_thread_fence(std::memory_order_acquire);
  assert(data == 42);
}

我想知道为什么 isDataReady 上存在数据竞争。 通常,正确的代码应该是对原子 bool 变量使用 relaxed 排序。

是不是因为isDataReady的写(事务)在读之前没有完成?即使是这样,这真的是个问题吗?

TL;DR

这种数据竞争很危险,您应该注意消除它。它可能不会因为你的运气而表现出来,但它最终会让人头疼。

长一点

由于以下几个问题,此代码存在问题:

  1. 虽然编译 Consumer 编译器不知道 isDataReady 可以在后台更改,因此发出 while(!isDataReady) 无限循环或什么都不发出是完全合理的(由于 forward progress guarantee,正如评论中指出的那样)。

  2. 如果写入 and/or 读取到 bool 不是原子的(在大多数平台上不是这种情况,但理论上是可能的)任何读取都可能导致获取垃圾数据.

  3. 带有 std::memory_order_release 的内存栅栏确保线程中发生的更改在其他线程调用带有 std::memory_order_acquire 的栅栏后可见(至少在简化中)。因此,bool变量的变化在其他线程中可能是不可见的。

  4. 由于现代处理器的超标量架构,操作可能会在运行时由处理器重新排序。因此从 Consumer 可见的 Producer 中的内存写入顺序可能与代码中的顺序不同。