Template:cpp/atomic/sequentially-consistent

来自cppreference.com

被标为 memory_order_seq_cst 的原子操作不仅以与释放-获得定序相同的方式进行内存定序(在一个线程中先发生于存储的任何副作用都变成进行加载的线程中的可见副作用),还对所有带此标签的内存操作建立了一个单独全序

正式而言,

对原子对象 M 进行加载的每个 memory_order_seq_cst 操作 B,均会观测到以下之一:

  • 修改 M 的上个操作 A 的结果,A 在单独全序中先出现于 B,
  • 或者,若存在这种 A,则 B 可能观测到 M 上的某次修改结果,此次修改非 memory_order_seq_cst 而且不先发生于 A,
  • 或者,若不存在这种 A,则 B 可能观测到 M 上的某次无关联修改的结果,此次修改非 memory_order_seq_cst

若存在 memory_order_seq_cststd::atomic_thread_fence 操作 X 先序于 B,则 B 观测到以下之一:

  • 在单独全序中先出现于 X 的上个 M 的 memory_order_seq_cst 修改,
  • 在单独全序中后出现于它的某次 M 的无关联修改。

设有 M 上的一对原子操作,称之为 A 和 B,这里 A 写入、B 读取 M 的值,若存在二个 memory_order_seq_cststd::atomic_thread_fence X 和 Y,且若 A 先序于 X,Y 先序于 B,且 X 在单独全序中先出现于 Y,则 B 观测到二者之一:

  • A 的效应,
  • 在 M 的修改顺序中后出现于 A 的某次无关联修改。

设有 M 上的一对原子操作,称之为 A 和 B,若符合下列条件之一,则 M 的修改顺序中 B 先发生于 A:

  • 存在一个 memory_order_seq_cststd::atomic_thread_fence X,它满足 A 先序于 X,且 X 在单独全序中先出现于 B,
  • 或者,存在一个 memory_order_seq_cststd::atomic_thread_fence Y,它满足 Y 先序于 B,且 A 在单独全序中先出现于 Y,
  • 或者,存在 memory_order_seq_cststd::atomic_thread_fence X 和 Y,它们满足 A 先序于 X,Y 先序于 B,且 X 在单独全序中先出现于 Y。

注意这表明:

1) 一旦出现未标记 memory_order_seq_cst 的原子操作,则立即丧失序列一致性,
2) 序列一致栅栏仅为栅栏自身建立全序,而不为通常情况下的原子操作建立(先序于 不是跨线程关系,不同于先发生于
(C++20 前)
正式而言,

某原子对象 M 上的原子操作连贯先序于 M 上的另一原子操作 B,若下列任一为真:

1) A 是修改,而 B 读取 A 所存储的值,
2) A 在 M 的修改顺序中前于 B,
3) A 读取原子操作 X 所存储的值,而 X 在修改顺序中前于 B,且 A 与 B 不是同一读修改写操作,
4) A 连贯先序于 X,而 X 连贯先序于 B。

所有 memory_order_seq_cst 操作,包括栅栏的,有一个单独全序 S,它满足下列约束:

1) 若 A 与 B 为 memory_order_seq_cst 操作,而 A 强先发生于 B,则 A 在 S 中前于 B,
2) 对于对象 M 上的每对原子操作 A 与 B,其中 A 连贯先序于 B:
a) 若 A 与 B 都是 memory_order_seq_cst 操作,则 S 中 A 前于 B,
b) 若 A 是 memory_order_seq_cst 操作,而 B 先发生于 memory_order_seq_cst 栅栏 Y,则 S 中 A 前于 Y,
c)memory_order_seq_cst 栅栏 X 先发生于 A,而 B 为 memory_order_seq_cst 操作,则 S 中 X 前于 B,
d)memory_order_seq_cst 栅栏 X 先发生于 A,而 B 先发生于 memory_order_seq_cst 栅栏 Y,则 S 中 X 前于 Y。

正式定义确保:

1) 单独全序与任何原子对象的修改顺序一致。
2) memory_order_seq_cst 加载到的值,要么来自最后一次 memory_order_seq_cst 修改,要么来自某个不先发生于顺序中之前的 memory_order_seq_cst 修改操作的非 memory_order_seq_cst 修改。

单独全序可能与先发生于不一致。这允许 memory_order_acquirememory_order_release 在某些 CPU 上的更高效实现。当 memory_order_acquirememory_order_releasememory_order_seq_cst 混合时,这能产生令人惊讶的结果。

例如,对于初值为零的 xy

// 线程 1 :
x.store(1, std::memory_order_seq_cst); // A
y.store(1, std::memory_order_release); // B
// 线程 2 :
r1 = y.fetch_add(1, std::memory_order_seq_cst); // C
r2 = y.load(std::memory_order_relaxed); // D
// 线程 3 :
y.store(3, std::memory_order_seq_cst); // E
r3 = x.load(std::memory_order_seq_cst); // F

允许这些操作产生 r1 == 1 && r2 == 3 && r3 == 0 的结果,其中 A 先发生于 C,但 memory_order_seq_cst 的单独全序 C-E-F-A 中 C 前于 A(见 Lahav 等)。

注意:

1) 一旦出现未标记 memory_order_seq_cst 的原子操作,程序的序列一致保证就会立即丧失,
2) 多数情况下,memory_order_seq_cst 原子操作相对于同一线程所进行的其他原子操作可重排。
(C++20 起)

在多生产者-多消费者的情形中,若所有消费者都必须以相同顺序观察到所有生产者的动作出现,则可能必须进行序列定序。

全序列定序在所有多核系统上都要求完全的内存栅栏 CPU 指令。这可能成为性能瓶颈,因为它强制受影响的内存访问传播到每个核心。