如何在c++中定义变量的修改顺序?



我看过这个问题&A: 'strong happens before'与'(简单地)发生在'

作者给出了一个有趣的求值的大纲,这个求值在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的修改顺序是什么,但是我不知道如何确定。(或者这次评估y可能有多个修改顺序?…)

更一般地说,c++中原子变量的修改顺序是如何定义的?例如:https://en.cppreference.com/w/cpp/atomic/memory_order

写-写一致性:如果修改某个原子M的求值A(写)发生在修改M的求值B之前,那么在M

的修改顺序中,A出现在B之前

因此,修改顺序似乎必须与write-write happens-before一致。

是唯一定义修改顺序的东西吗?

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

y(x 12)的mod顺序(对于这个评估)?

我相信这也可能有助于推理seq_cst的顺序…

对象的修改顺序是线程在运行while(1) { y.load(relaxed); }的紧循环中旋转时看到的顺序,并且碰巧看到每一个更改。(并不是说这是一种有效的观察方法,但是每个对象都有自己的修改顺序,所有线程都可以达成一致,就像在真正的cpu上一样,由于MESI独占所有权需要将存储提交到L1d缓存。或者通过POWER cpu上的SMT线程之间的私有存储转发提前看到它。

一些随机的事实:

  • 单个对象的修改顺序与一个线程内的程序顺序兼容,即使使用relaxed
  • 当一个线程看到一个带有load的值后,它只能看到这个值或修改顺序之后的值,即使load和store是relaxed
  • 一个变量的观测值不能超过其他线程存储的次数。如果您有来自一堆线程的一堆存储,其中一个将是最后一个,这是所有读取器(最终)将看到的值。当尘埃落定时,任何给定的读者都不会看到值来回变化,而不是实际看到后来的存储在mod顺序中。(参见"连贯性"[导言]中的规则。

我认为这个评估显示了实际的有效顺序,所以y的mod顺序只是从上到下读取,由操作(2) (x) (1)按该顺序存储的值。(因为它使用了足够多的seq_cst操作,所有线程都可以在顺序上达成一致,并且显示其他一些事情最终将发布存储(x)排在seq_cst存储(2)之后。)

这个eval命令是说(2)存储在(x)存储之前是可见的,所以(x)存储取代了它。

y.fetch_add(1)之前尘埃落定,否则它将与(2)而不是(x)同步。

更正,显然他们显示(x) (1) (2)的修改顺序是合法的,尽管在(2)(存储3)和最终的y.load()之间发生了不同口味的链。所以这个store在fetch_add之后,load之前对T2是可见的。

最新更新