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

Does std::atomic provide atomic behavior, regardless of ordering?

如果使用 std::atomic 模板声明变量,例如 std::atomic<int>,是否保证通过 std::atomic 中的方法访问将导致 consistent 值(即通过 std::atomic 方法写入一个 write)而不考虑顺序?

据我所知,这相当于询问 readswrites 是否可以是 "torn" - 也就是说,书面或真实的多个部分在 ISA 级别可见。

Atomic,from the Greek atom meaning indivisible,与 "no tearing" 同义。这意味着整个操作不可分割地发生。 你可以用 std::atomic 类型做的一切都是原子的(没有撕裂)。

C++14 draft N4140 部分 29.3 顺序和一致性 是该章的第一部分,深入细节。第一点是 (1.4):

[ Note: Atomic operations specifying memory_order_relaxed are relaxed with respect to memory ordering. Implementations must still guarantee that any given atomic access to a particular atomic object be indivisible with respect to all other atomic accesses to that object. — end note ]

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

§ 29.6.5 Requirements for operations on atomic types

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. Requires: The order argument shall not be memory_order_consume, memory_order_acquire, nor memory_order_acq_rel.
  2. Effects: Atomically replaces the value pointed to by object or by this with the value of desired. Memory is affected according to the value of order.

依此类推,在所有适用的情况下使用 Atomically 这个词。

而不是为 add/+sub/- 以及 |&& 重复自己^,他们有一个key/op table适用于这个块:

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;
  • 28 Effects: Atomically replaces the value pointed to by object or by this with the result of the computation applied to the value pointed to by object or by this and the given operand. Memory is affected according to the value of order. These operations are atomic read-modify-write operations (1.10).
  • 29 Returns: Atomically, the value pointed to by object or by this immediately before the effects.
  • 30 Remark: For signed integer types, arithmetic is defined to use two’s complement representation. There are no undefined results. For address types, the result may be an undefined address, but the operations otherwise have no undefined behavior.

唯一可选的是在其他线程中与 loads/stores 进行排序/同步(对于没有同步的原子性,请使用 memory_order_relaxed)。

事实上,没有办法 "turn off" 原子性来加载昂贵的宽类型(例如在 x86 上 之前)。我在那个答案中使用了一个 union hack 来有效地加载结构。

更重要的是,联合是 gcc 不优化 ptr_and_counter.ptr 仅加载指针的解决方法,我认为这至少在 x86 上是安全的。相反,gcc 坚持以原子方式加载整个结构,然后 then 从结果中获取指针。当它是 x86-64 上的 16 字节结构时,这是非常糟糕的,而在 x86-32 上则相当糟糕。 (参见 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80835