在 C++ 中,acquire/release 原子访问与宽松访问结合栅栏之间是否有任何有效区别?
In C++, is there any effective difference between a acquire/release atomic access and a relaxed access combined with a fence?
具体来说,有没有有效的区别:
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);
分别是?
非宽松原子访问是否提供信号栅栏和线程栅栏?
你需要
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);
Non-relaxed 原子访问确实提供信号栅栏和线程栅栏。
在你的代码中,对于load
和store
,fence和atomic操作的顺序应该是相反的,然后它类似于standalone操作,但有区别。
对原子变量的获取和释放操作充当 one-way 障碍,但方向相反。
也就是说,store/release 操作会阻止在它之前(在程序源中)的内存操作在它之后重新排序,
而 load/acquire 操作会阻止其后的内存操作在其之前重新排序。
// 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 可见并且同步完成。
作为 one-way 屏障,对 x
的写入可以连接甚至先于内存操作 A,但由于它不是 acquire/release 关系的一部分 x
不能可靠由线程 2 访问。
用独立线程栅栏和宽松操作替换原子操作是类似的:
// 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
这实现了相同的结果,但一个重要的区别是两个栅栏都不充当 one-way 障碍;
如果他们这样做了,到 a
的原子存储可以在释放栅栏之前重新排序,并且来自 a
的原子加载可以在获取栅栏之后重新排序,并且
那会破坏同步关系。
总的来说:
- 独立的发布围栏可以防止前面的操作被后面的(原子)存储重新排序。
- 一个独立的获取栅栏可以防止后面的操作被前面的(原子)加载重新排序。
标准允许 Acquire/Release 栅栏与 Acquire/Release 操作混合。
Do non-relaxed atomic accesses provide signal fences as well as thread fences?
我不太清楚你在这里问什么,因为线程栅栏通常用于宽松的原子操作,
但是 std::thread_signal_fence
类似于 std::atomic_thread_fence
,除了它应该在同一个线程中运行并且
因此,编译器不会为 inter-thread 同步生成 CPU 指令。
它基本上充当 compiler-only 屏障。
具体来说,有没有有效的区别:
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);
分别是?
非宽松原子访问是否提供信号栅栏和线程栅栏?
你需要
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);
Non-relaxed 原子访问确实提供信号栅栏和线程栅栏。
在你的代码中,对于load
和store
,fence和atomic操作的顺序应该是相反的,然后它类似于standalone操作,但有区别。
对原子变量的获取和释放操作充当 one-way 障碍,但方向相反。 也就是说,store/release 操作会阻止在它之前(在程序源中)的内存操作在它之后重新排序, 而 load/acquire 操作会阻止其后的内存操作在其之前重新排序。
// 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 可见并且同步完成。
作为 one-way 屏障,对 x
的写入可以连接甚至先于内存操作 A,但由于它不是 acquire/release 关系的一部分 x
不能可靠由线程 2 访问。
用独立线程栅栏和宽松操作替换原子操作是类似的:
// 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
这实现了相同的结果,但一个重要的区别是两个栅栏都不充当 one-way 障碍;
如果他们这样做了,到 a
的原子存储可以在释放栅栏之前重新排序,并且来自 a
的原子加载可以在获取栅栏之后重新排序,并且
那会破坏同步关系。
总的来说:
- 独立的发布围栏可以防止前面的操作被后面的(原子)存储重新排序。
- 一个独立的获取栅栏可以防止后面的操作被前面的(原子)加载重新排序。
标准允许 Acquire/Release 栅栏与 Acquire/Release 操作混合。
Do non-relaxed atomic accesses provide signal fences as well as thread fences?
我不太清楚你在这里问什么,因为线程栅栏通常用于宽松的原子操作,
但是 std::thread_signal_fence
类似于 std::atomic_thread_fence
,除了它应该在同一个线程中运行并且
因此,编译器不会为 inter-thread 同步生成 CPU 指令。
它基本上充当 compiler-only 屏障。