在C++中,获取/释放原子访问和结合围栏的放松访问之间有什么有效的区别吗



具体而言,之间是否存在任何有效差异

i = a.load(memory_order_acquire);

a.store(5, memory_order_release);

atomic_thread_fence(memory_order_acquire);
i = a.load(memory_order_relaxed);

a.store(5, memory_order_relaxed);
atomic_thread_fence(memory_order_release);

分别?

非宽松的原子访问提供信号围栏和线程围栏吗?

在您的代码中,对于loadstore,围栏和原子操作之间的顺序应该颠倒,然后它与独立操作类似,但存在差异。

对原子变量的获取和释放操作充当单向屏障,但方向相反。也就是说,存储/释放操作防止在它之前(在程序源中)的内存操作在它之后被重新排序,而加载/获取操作可防止后面的内存操作在它之前被重新排序。

// thread 1
// shared memory operations A
a.store(5, std::memory_order_release);
x = 42; // regular int

// thread 2
while (a.load(std::memory_order_acquire) != 5);
// shared memory operations B

内存操作A不能向下移动到store/release以下,而内存操作B不能向上移动到load/acquire以上。一旦线程2读取5,内存操作A对B可见,同步就完成了
作为单向屏障,对x的写入可以加入甚至先于内存操作a,但由于它不是获取/释放关系的一部分,线程2无法可靠地访问x

用独立的线程围栏和宽松的操作取代原子操作是类似的:

// thread 1
// shared memory operations A
std::atomic_thread_fence(memory_order_release);
a.store(5, std::memory_order_relaxed);

// thread 2
while (a.load(std::memory_order_relaxed) != 5);
std::atomic_thread_fence(memory_order_acquire);
// shared memory operations B

这实现了相同的结果,但一个重要的区别是,两个围栏都不是单向屏障;如果他们这样做了,则到a的原子存储可以在释放围栏之前重新排序,来自a的原子加载可以在获取围栏之后重新排序,并且这将打破同步关系。

一般情况下:

  • 一个独立的发布围栏可以防止前面的操作被后面的(原子)存储重新排序
  • 独立的获取围栏可以防止以下操作被前面的(原子)加载重新排序

该标准允许采集/释放围栏与采集/释放操作混合使用。

非松弛原子访问是否提供信号围栏和线程围栏?

我不完全清楚你在这里问什么,因为线程围栏通常与宽松的原子操作一起使用,但是std::thread_signal_fence类似于std::atomic_thread_fence,不同之处在于它应该在同一线程内操作,并且因此编译器不生成用于线程间同步的CPU指令。它基本上只充当编译器的屏障。

您需要

atomic_thread_fence(memory_order_release);
a.store(5, memory_order_relaxed);

i = a.load(memory_order_relaxed);
atomic_thread_fence(memory_order_acquire);

更换

a.store(5, memory_order_release);

i = a.load(memory_order_acquire);

非松弛原子访问确实提供了信号围栏以及线程围栏。

最新更新