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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子线程围栏:为什么在这个非原子变量上存在数据竞争?这有关系吗?相关的知识,希望对你有一定的参考价值。

假设我们有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的write(事务)可能在读取之前没有完成吗?即使是这样,它真的是一个问题吗?

答案

TL;DR

这种数据竞争是危险的,您应该关心消除它。它可能不会因你的运气而显现,但它最终会引起头痛。

A bit longer

由于一些问题,此代码存在问题:

  1. 虽然编译Consumer编译器并不知道isDataReady可以在背景中改变,所以将while(!isDataReady)发出无限循环或者什么都没有是完全合理的(由于forward progress guarantee,正如评论中指出的那样)。
  2. 如果对bool的写入和/或读取不是原子的(在大多数平台上都不是这种情况,但理论上是可行的)任何读取都可能导致获取垃圾数据。
  3. 使用std::memory_order_release的内存栅栏可确保在其他线程使用std::memory_order_acquire调用fence之后可以看到线程中发生的更改(至少在简化中)。因此,bool变量的变化在其他线程中可能是不可见的。
  4. 由于现代处理器的超标量体系结构,操作可以在运行时由处理器重新排序。因此,从Producer可见的Consumer中的内存写入顺序可能与放在代码中的顺序不同。

以上是关于原子线程围栏:为什么在这个非原子变量上存在数据竞争?这有关系吗?的主要内容,如果未能解决你的问题,请参考以下文章

java并发编程11.原子变量与非阻塞同步机制

java并发编程原子变量和非阻塞的同步机制

非原子变量的障碍和同步点——数据竞争?

第十五章 原子变量和非阻塞同步机制

转Java线程安全

Java并发编程-非阻塞同步方式原子类(Atomic)的使用