具体而言,之间是否存在任何有效差异
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);
分别?
非宽松的原子访问提供信号围栏和线程围栏吗?
在您的代码中,对于load
和store
,围栏和原子操作之间的顺序应该颠倒,然后它与独立操作类似,但存在差异。
对原子变量的获取和释放操作充当单向屏障,但方向相反。也就是说,存储/释放操作防止在它之前(在程序源中)的内存操作在它之后被重新排序,而加载/获取操作可防止后面的内存操作在它之前被重新排序。
// 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);
非松弛原子访问确实提供了信号围栏以及线程围栏。