atomic_thread_fence(memory_order_release) 与使用 memory_order_acq_rel 有区别吗?
Is atomic_thread_fence(memory_order_release) different from using memory_order_acq_rel?
cppreference.com 提供 this note about std::atomic_thread_fence
(强调我的):
atomic_thread_fence imposes stronger synchronization constraints than an atomic store operation with the same std::memory_order.
While an atomic store-release operation prevents all preceding writes from moving past the store-release, an atomic_thread_fence with memory_order_release ordering prevents all preceding writes from moving past all subsequent stores.
我理解这条注释的意思是 std::atomic_thread_fence(std::memory_order_release)
不是单向的,就像商店发布一样。这是一个双向栅栏,可防止栅栏任一侧 的商店重新排序,越过栅栏另一侧的商店。
如果我理解正确的话,这个围栏似乎提供了与 atomic_thread_fence(memory_order_acq_rel)
相同的保证。它是一个 "upward" 栅栏,一个 "downward" 栅栏。
std::atomic_thread_fence(std::memory_order_release)
和 std::atomic_thread_fence(std::memory_order_acq_rel)
在功能上有区别吗?还是仅仅是美学上的差异,以记录代码的目的?
独立栅栏比具有相同排序约束的原子操作强加了排序,但这不会改变强制执行排序的方向。
Bot 一个原子释放操作和一个独立的释放栅栏是单向的,
但是原子操作对自身进行排序,而原子栅栏则对其他商店进行排序。
例如,具有释放语义的原子操作:
std::atomic<int> sync{0};
// memory operations A
sync.store(1, std::memory_order_release);
// store B
这保证了 A(加载和存储)的任何内存操作部分都不能(明显地)用原子存储本身重新排序。
但它是单向的,没有排序规则适用于在原子操作之后排序的内存操作;因此,仍然可以使用 A 中的任何内存操作对存储 B 进行重新排序。
一个独立的发布围栏改变了这个行为:
// memory operations A
std::atomic_thread_fence(std::memory_order_release);
// load X
sync.store(1, std::memory_order_relaxed);
// stores B
这保证了 A 中的任何内存操作都不能(明显地)用 any 在释放栅栏之后排序的存储重新排序。
在这里,B 的存储不能再用 A 中的任何内存操作重新排序,因此,释放栅栏比原子释放操作更强大。
但它也是单向的,因为来自 X 的负载仍然可以通过 A 中的任何内存操作重新排序。
区别很细微,通常原子释放操作优于独立释放栅栏。
独立获取栅栏的规则类似,只是它强制执行相反方向的排序并在负载上运行:
// loads B
sync.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
// memory operations A
在独立获取栅栏之前排序的 任何 加载都不能对 A 中的内存操作进行重新排序。
具有 std::memory_order_acq_rel
排序的独立围栏结合了获取和释放围栏的逻辑。
// memory operations A
// load A
std::atomic_thread_fence(std::memory_order_acq_rel);
// store B
//memory operations B
但是,一旦您意识到 A 中的商店仍然可以通过 B 中的负载重新订购,这就会变得非常棘手。
Acq/rel 应该避免栅栏以支持常规的原子操作,或者更好的互斥锁。
cppreference.com 在您引用的段落中有一些错误。我在下面突出显示了它们:
atomic_thread_fence imposes stronger synchronization constraints than an atomic store operation with the same std::memory_order. While an atomic store-release operation prevents all preceding writes(should be memory operations, i.e. including reads and writes) from moving past the store-release (the complete sentence should be: the store-release operation itself), an atomic_thread_fence with memory_order_release ordering prevents all preceding writes(should be memory operations, i.e. including reads and writes) from moving past all subsequent stores.
换句话说:
The release operation actually places fewer memory ordering constraints on neighboring operations than the release fence. A release operation only needs to prevent preceding memory operations from being reordered past itself, but a release fence must prevent preceding memory operations from being reordered past all subsequent writes. Because of this difference, a release operation can never take the place of a release fence.
本文引用自here。
这是我对以下文字的意图的解释,我认为这是有意的。此外,该解释在内存模型方面是正确的,但仍然很糟糕 ,因为它是一个不完整的解释。
While an atomic store-release operation prevents all preceding writes
from moving past the store-release, an atomic_thread_fence
with
memory_order_release
ordering prevents all preceding writes from
moving past all subsequent stores.
"store" 与 "writes" 的使用是有意的:
- "store",在这里,意味着存储在
std::atomic<>
对象上(不仅仅是对 std::atomic<>::store
的调用,还有等同于 .store(value)
或 RMW 的赋值原子操作);
- "write",在这里,意味着任何内存写入,无论是正常的(非原子的)还是原子的。
It's a bidirectional fence, preventing stores on either side of the
fence from reordering past a store on the other side of the fence.
不,您错过了一个本质区别,因为它只是隐含的;表达不清楚,太微妙 - 不适合作为教学文本!
它说释放栅栏 不是 对称的:以前的内存副作用,称为 "writes",受以下 atomic store 操作。
即使有这样的澄清,它仍然不完整,所以这是一个错误的解释:它强烈建议存在释放栅栏只是为了确保写入(和仅写入)完成 .事实并非如此。
释放操作就是我所说的:"I'm done there"信号。它表示所有先前的内存操作已完成、完成、可见。重要的是要理解,不仅修改(可以通过查看内存状态检测)是有序的,内存中的所有内容都需要。
许多关于线程原语的文章都存在这种缺陷。
cppreference.com 提供 this note about std::atomic_thread_fence
(强调我的):
atomic_thread_fence imposes stronger synchronization constraints than an atomic store operation with the same std::memory_order.
While an atomic store-release operation prevents all preceding writes from moving past the store-release, an atomic_thread_fence with memory_order_release ordering prevents all preceding writes from moving past all subsequent stores.
我理解这条注释的意思是 std::atomic_thread_fence(std::memory_order_release)
不是单向的,就像商店发布一样。这是一个双向栅栏,可防止栅栏任一侧 的商店重新排序,越过栅栏另一侧的商店。
如果我理解正确的话,这个围栏似乎提供了与 atomic_thread_fence(memory_order_acq_rel)
相同的保证。它是一个 "upward" 栅栏,一个 "downward" 栅栏。
std::atomic_thread_fence(std::memory_order_release)
和 std::atomic_thread_fence(std::memory_order_acq_rel)
在功能上有区别吗?还是仅仅是美学上的差异,以记录代码的目的?
独立栅栏比具有相同排序约束的原子操作强加了排序,但这不会改变强制执行排序的方向。
Bot 一个原子释放操作和一个独立的释放栅栏是单向的, 但是原子操作对自身进行排序,而原子栅栏则对其他商店进行排序。
例如,具有释放语义的原子操作:
std::atomic<int> sync{0};
// memory operations A
sync.store(1, std::memory_order_release);
// store B
这保证了 A(加载和存储)的任何内存操作部分都不能(明显地)用原子存储本身重新排序。 但它是单向的,没有排序规则适用于在原子操作之后排序的内存操作;因此,仍然可以使用 A 中的任何内存操作对存储 B 进行重新排序。
一个独立的发布围栏改变了这个行为:
// memory operations A
std::atomic_thread_fence(std::memory_order_release);
// load X
sync.store(1, std::memory_order_relaxed);
// stores B
这保证了 A 中的任何内存操作都不能(明显地)用 any 在释放栅栏之后排序的存储重新排序。 在这里,B 的存储不能再用 A 中的任何内存操作重新排序,因此,释放栅栏比原子释放操作更强大。 但它也是单向的,因为来自 X 的负载仍然可以通过 A 中的任何内存操作重新排序。
区别很细微,通常原子释放操作优于独立释放栅栏。
独立获取栅栏的规则类似,只是它强制执行相反方向的排序并在负载上运行:
// loads B
sync.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
// memory operations A
在独立获取栅栏之前排序的 任何 加载都不能对 A 中的内存操作进行重新排序。
具有 std::memory_order_acq_rel
排序的独立围栏结合了获取和释放围栏的逻辑。
// memory operations A
// load A
std::atomic_thread_fence(std::memory_order_acq_rel);
// store B
//memory operations B
但是,一旦您意识到 A 中的商店仍然可以通过 B 中的负载重新订购,这就会变得非常棘手。 Acq/rel 应该避免栅栏以支持常规的原子操作,或者更好的互斥锁。
cppreference.com 在您引用的段落中有一些错误。我在下面突出显示了它们:
atomic_thread_fence imposes stronger synchronization constraints than an atomic store operation with the same std::memory_order. While an atomic store-release operation prevents all preceding writes(should be memory operations, i.e. including reads and writes) from moving past the store-release (the complete sentence should be: the store-release operation itself), an atomic_thread_fence with memory_order_release ordering prevents all preceding writes(should be memory operations, i.e. including reads and writes) from moving past all subsequent stores.
换句话说:
The release operation actually places fewer memory ordering constraints on neighboring operations than the release fence. A release operation only needs to prevent preceding memory operations from being reordered past itself, but a release fence must prevent preceding memory operations from being reordered past all subsequent writes. Because of this difference, a release operation can never take the place of a release fence.
本文引用自here。
这是我对以下文字的意图的解释,我认为这是有意的。此外,该解释在内存模型方面是正确的,但仍然很糟糕 ,因为它是一个不完整的解释。
While an atomic store-release operation prevents all preceding writes from moving past the store-release, an
atomic_thread_fence
withmemory_order_release
ordering prevents all preceding writes from moving past all subsequent stores.
"store" 与 "writes" 的使用是有意的:
- "store",在这里,意味着存储在
std::atomic<>
对象上(不仅仅是对std::atomic<>::store
的调用,还有等同于.store(value)
或 RMW 的赋值原子操作); - "write",在这里,意味着任何内存写入,无论是正常的(非原子的)还是原子的。
It's a bidirectional fence, preventing stores on either side of the fence from reordering past a store on the other side of the fence.
不,您错过了一个本质区别,因为它只是隐含的;表达不清楚,太微妙 - 不适合作为教学文本!
它说释放栅栏 不是 对称的:以前的内存副作用,称为 "writes",受以下 atomic store 操作。
即使有这样的澄清,它仍然不完整,所以这是一个错误的解释:它强烈建议存在释放栅栏只是为了确保写入(和仅写入)完成 .事实并非如此。
释放操作就是我所说的:"I'm done there"信号。它表示所有先前的内存操作已完成、完成、可见。重要的是要理解,不仅修改(可以通过查看内存状态检测)是有序的,内存中的所有内容都需要。
许多关于线程原语的文章都存在这种缺陷。