memory_order_seq_cst 栅栏什么时候有用?

cur*_*guy 6 c++ multithreading memory-model memory-barriers stdatomic

C++ 支持原子线程栅栏,即保证使用std::atomic<>操作的线程的属性的栅栏,函数atomic_thread_fence. 它需要一个记忆顺序参数来调整围栏的“强度”。

我知道当并非所有原子操作都以“强”顺序完成时,围栏很有用

  • 当线程中并非所有原子读取 (1) 都是获取操作时,您可能会发现获取栅栏的用途;
  • 当线程中并非所有原子修改 (1) 都是释放操作时,您可能会发现释放栅栏的用途。

(1) 包括 RMW 操作

所以所有这些(acquire、release 和 acq_rel 栅栏)的用处是显而易见的:它们允许使用比 acq/rel 弱的原子操作的线程(分别)正确同步。

但我不明白哪里memory_order_seq_cst特别需要作为围栏:

  • 使用弱于memory_order_seq_cst原子操作和memory_order_seq_cst栅栏的含义是什么?

  • 不能保证的memory_order_seq_cst栅栏会特别保证什么(就原子操作的可能排序而言)memory_order_acq_rel

mpo*_*ter 3

不,seq-cst-fence 不仅是发布和获取围栏,而且还提供一些附加属性(请参阅工作草案,编程语言 C++ 标准,32.4.4-32.4.8)。seq-cst 栅栏也是所有顺序一致操作的单一总顺序的一部分,强制执行以下观察:

  • 对于读取原子对象M的值的原子操作B ,如果在B之前有一个栅栏X排序,则B会观察总顺序S中X之前的M的最后一个修改,或者在其修改中观察M的后续修改命令。memory_order_seq_cstmemory_order_seq_cst
  • 对于原子对象M上的原子操作AB,其中A修改MB获取其值,如果存在栅栏X ,使得AS中的X之前排序,而B在 S 中的X之后,则B观察到A的效果或者按其修改顺序对M进行后续修改。memory_order_seq_cst
  • 对于原子对象M上的原子操作AB,其中A修改MB获取其值,如果存在栅栏XY ,使得A在X之前排序,Y在B之前排序,并且在S中X在Y之前,然后B观察A的效果或按其修改顺序对M进行后续修改的效果。memory_order_seq_cst

例如,我在危险指针实现中使用 seq-cst 栅栏:https://github.com/mpoeter/xenium/blob/master/xenium/reclamation/impl/hazard_pointer.hpp
线程获取对某个对象的安全引用在存储危险指针之后、重新读取指向对象的指针之前使用 seq-cst fence。尝试回收某些对象的线程在从所有线程收集活动危险指针之前使用 seq-cst 栅栏。基于上面的规则,这确保了尝试回收对象的线程看到其他线程具有该对象的 HP(即该对象已使用),或者重新加载尝试获取对该对象的安全引用的线程返回一个不同的指针,向该线程指示该对象已被删除并且必须执行重试。