std :: atomic是否提供原子行为,无论是否排序?

时间:2017-08-31 02:39:58

标签: c++ concurrency memory-model stdatomic

如果使用std::atomic模板声明变量,例如std::atomic<int>,则保证通过std::atomic中的方法进行访问会导致一致值(即通过std::atomic方法写入的值),无论排序如何?

据我所知,这相当于询问读取写入是否可以“撕裂” - 即在ISA可见的多个部分中写入或真实水平。

1 个答案:

答案 0 :(得分:5)

Atomic,from the Greek atom meaning indivisible,是“不撕裂”的代名词。这意味着整个操作不可避免地发生。  使用std::atomic类型可以执行的所有操作始终是原子的(不会撕裂)。

C++14 draft N4140部分 29.3订单和一致性是该章的第一部分,详细介绍。最重要的一点是(1.4):

  

[注意:指定memory_order_relaxed的原子操作在内存排序方面是放宽的。   实现必须仍然保证对特定原子对象的任何给定原子访问都是不可分割的   关于对该对象的所有其他原子访问。 - 结束说明]

就规定原子性要求的技术语言而言,每个操作(如.store().load().fetch_add())都使用以下语言定义:

  

§ 29.6.5原子类型操作要求

void atomic_store(volatile A * object, C desired) noexcept;
void atomic_store(A * object, C desired) noexcept;
void atomic_store_explicit(volatile A * object, C desired, memory_order order) noexcept;
void atomic_store_explicit(A * object, C desired, memory_order order) noexcept;
void A ::store(C desired, memory_order order = memory_order_seq_cst) volatile noexcept;
void A ::store(C desired, memory_order order = memory_order_seq_cst) noexcept;
     
      
  1. 要求:订单参数不应为memory_order_consumememory_order_acquire,也不应为memory_order_acq_reladd   +
  2.   
  3. 效果:以原子方式替换object指向的值,或者替换为desired的值。   内存受到订单价值的影响。
  4.   

依此类推,在每种情况下都使用 Atomically 这个词。

而不是重复sub / -| / &,而不是^C atomic_fetch_key (volatile A * object, M operand) noexcept; C atomic_fetch_key (A * object, M operand) noexcept; C atomic_fetch_key _explicit(volatile A * object, M operand, memory_order order) noexcept; C atomic_fetch_key _explicit(A * object, M operand, memory_order order) noexcept; C A ::fetch_key (M operand, memory_order order = memory_order_seq_cst) volatile noexcept; C A ::fetch_key (M operand, memory_order order = memory_order_seq_cst) noexcept; 和{{ 1}},它们有一个适用于这个块的key / op表:

memory_order_relaxed
     
      
  • 28 效果:以原子方式替换object或by指向的值   这与计算结果应用于指向的值   通过对象或由此和给定的操作数。记忆受到影响   根据订单的价值。这些操作是原子的   读 - 修改 - 写操作(1.10)。
  •   
  • 29 返回:原子,值   在对象之前指向或在效果之前立即指向。
  •   
  • 30   备注:对于有符号整数类型,算术被定义为使用两个   补充表示。没有未定义的结果。地址   类型,结果可能是未定义的地址,但操作   否则没有未定义的行为。
  •   

唯一可选的是在其他线程中对加载/存储进行排序/同步(对于没有同步的原子性,使用ptr_and_counter.ptr)。

事实上,没有办法“关闭”原子性来加载昂贵的宽类型(例如在x86之前a CAS on atomic<pointer_and_ABAcounter> which compiles to lock cmpxchg16b之前)。我在该答案中使用了一个union hack来有效地加载结构。

更重要的是,union是gcc没有优化0 1 2 3 4 5 6 7 8 9 10 Nan 6 5 8 9 2 Nan Nan Nan Nan Nan Nan 3 8 Nan Nan Nan Nan Nan Nan Nan Nan Nan 5 9 2 4 Nan Nan Nan Nan Nan Nan 到指针加载的一种解决方法,我认为至少在x86上是安全的。相反,gcc坚持原子加载整个结构,然后从结果中获取指针。当它在x86-64上是一个16字节的结构时非常糟糕,在x86-32上相当糟糕。 (见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80835