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

Posted

技术标签:

【中文标题】原子线程围栏:为啥这个非原子变量存在数据竞争?这有关系吗?【英文标题】:atomic thread fence: Why is there a data race on this non atomic variable? And does it matter?原子线程围栏:为什么这个非原子变量存在数据竞争?这有关系吗? 【发布时间】:2019-01-09 12:51:56 【问题描述】:

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

【问题讨论】:

IIRC,栅栏确保存储/加载的同步顺序。但是,您仍然可以从两个线程一次访问相同的内存位置(isDataReady 变量),其中一个线程更新它,这是数据竞争的定义(以及 C++ 中的 UB)。 @DanielLangr 如果存在数据竞争(即使它不是“问题”),它是 C++ 中的 UB? @AntoineMorrier 是的:eel.is/c++draft/intro.races#21.sentence-3. 你没有提到 CPU。 【参考方案1】:

TL;DR

这种数据竞赛很危险,您应该注意消除它。它可能由于你的运气而不会表现出来,但它最终会引起头痛。

有点长

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

    在编译 Consumer 时,编译器不知道 isDataReady 可以在后台更改,因此发出 while(!isDataReady) 无限循环或什么都不发出是完全合理的(由于 forward progress guarantee,正如所指出的那样以 cmets 为单位)。

    如果对bool 的写入和/或读取不是原子的(在大多数平台上并非如此,但理论上是可能的)任何读取都可能导致获取垃圾数据。

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

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

【讨论】:

由于前向进度保证,编译器不发射任何内容而不是无限循环(我相信)同样合理。 对于 1),使 isDataReady volatile 修复问题。 2) volatile 不够,需要原子操作。 3)同样,我需要一个原子(因为原子操作是在内存位置完成的),所以 bool 的变化在其他线程中变得可见。 4)由于我使用的是内存围栏,所以这不是一个真正的问题。我说的对吗? @AntoineMorrier AFAIK 1)y 2)y 3) 和 4) 是的,但是您必须正确放置内存栅栏。 @AntoineMorrier 是的,只需将第一条内存栅栏线向下移动,然后将第二条内存栅栏线移到上面的 dowhile 线上方,一切都会好的 @AntoineMorrier 如果您关心性能,您可能对非阻塞等待感兴趣(例如,由条件变量提供)。在循环中读取原子 bool 会有效地阻塞 CPU(自旋锁 - 忙于等待),这仅在您知道不能以其他方式使用该 CPU(内核)并且等待时间很短时才有用。

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

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

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

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

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

以下程序集是原子的,如果不是,为啥?

线程 - AtomicInteger