C++中变量的修改顺序是如何定义的?

How is modification order of a variable defined in C++?

我已阅读此问答:

作者概述了一个有趣的评估,该评估在 C++20 之前是不可能的,但显然从 C++20 开始是可能的:

.-- T3 y.store(3, seq_cst);                   --.                 (2)
|        |                                      | strongly
|        | sequenced before                     | happens
|        V                                      | before
|   T3 a = x.load(seq_cst); // a = 0    --.   <-'                 (3)
|                                         : coherence-
|                                         : ordered
|                                         : before
|   T1 x.store(1, seq_cst);             <-'   --. --.             (4)
|        |                                      |st |
|        | sequenced before                     |h  |
|        V                                      |b  |
| . T1 y.store(1, release);                   <-'   |             (x)
| |      :                                          | strongly
| |      : synchronizes with                        | happens
| |      V                                          | before
| > T2 b = y.fetch_add(1, seq_cst); // b = 1  --.   |             (1)
|        |                                      |st |
|        | sequenced before                     |h  |
|        V                                      |b  |
'-> T2 c = y.load(relaxed); // c = 3          <-' <-'

右边的数字表示一个可能的seq_cst订单(为了方便我添加了(x);这条线不参与SC订单,因为它不是SC操作)。

我试图理解这个例子中y的mod化顺序是什么,但我不知道如何确定它。 (或者此评估是否有多个可能的 mody 化顺序?..)

更一般地说,modC++ 中原子变量的化序是如何定义的? 例如有这个:https://en.cppreference.com/w/cpp/atomic/memory_order

Write-write coherence: If evaluation A that modifies some atomic M (a write) happens-before evaluation B that modifies M, then A appears earlier than B in the modification order of M

所以看来mod化顺序必须与先写先行一致。

它是唯一定义mod化顺序的东西吗?

在上面的例子中,AFAIU (2) 和 (1) 之间没有 happens-before ;那么在 y 的 mod 化顺序中哪个是第一个?

mod的顺序是y(x 1 2)(本次评价)吗?

我相信它也可能有助于推理 seq_cst 顺序...

对象的mod化顺序是线程在紧密循环运行 while(1) { y.load(relaxed); } 中旋转时看到的顺序,并且碰巧看到每一个变化。 (这并不是实际观察它的有用方法,但每个对象都有自己的 mod 所有线程都可以始终同意的化序,就像在真正的 CPU 上一样,因为需要 MESI 独占所有权才能将存储提交给 L1d缓存。或者通过 POWER CPU 上的 SMT 线程之间的私有 store-forwarding 尽早查看它。)

一些随机事实:

  • 单个对象的mod化顺序与一个线程内的程序顺序兼容,即使 relaxed
  • 线程看到一个带负载的值后,即使加载和存储是 relaxed.[=32,它也只能看到那个值或 modification order 后面的值=]
  • 观察到的变量值的变化次数不能超过其他线程存储的次数。如果你有一堆来自一堆线程的商店,其中一个将是最后一个,这就是所有 readers 将看到的值(最终)。在尘埃落定的同时,任何给定的 reader 都不会看到值来回变化,除了实际看到 mod 订单中后面的商店。 (参见 [intro.races] in the standard 中的“一致性”规则)

我认为此评估显示的是实际有效顺序,因此 y 的 mod 顺序只是从上到下阅读,2 x 1。 (因为它使用了足够多的 seq_cst 操作,所有线程都可以就顺序达成一致,并表明其他一些东西最终在 seq_cst 存储 (2) 之后对发布存储 (x) 进行排序。)

这个评估命令是说 (2) 商店确实在 (x) 商店之前变得可见,所以 (x) 商店取代了它。

尘埃落定在 y.fetch_add (1) 之前,否则它会有 synced-with (2) 而不是 (x)。