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

bvk5enib  于 2023-08-09  发布在  其他
关注(0)|答案(1)|浏览(113)

我读了这个Q&A:What is the significance of 'strongly happens before' compared to '(simply) happens before'?
作者给出了一个有趣的评估的大纲,这个评估在C20之前是不可能的,但显然可以从C20开始:

.-- 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-one一致。
它是唯一定义修改顺序的东西吗?
在上面的例子中,AFAIU在(2)和(1)之间没有happens-before;那么在y的修改顺序中,哪个是第一个?
是否为y(x 1 2)的模阶(对于此评估)?
我相信这也可能有助于推理seq_cst顺序。

7eumitmz

7eumitmz1#

对象的修改顺序是线程在运行while(1) { y.load(relaxed); }的紧密循环中旋转时看到的顺序,并且碰巧看到了每个更改。(这并不是实际观察它的有用方法,但每个对象都有自己的修改顺序,所有线程都可以始终同意,就像在真实的的CPU上一样,由于MESI独占所有权需要将存储提交到L1 d缓存。或者通过POWER CPU上SMT线程之间的私有存储转发提前看到它。)
一些随机事实:

  • 单个对象的修改顺序与一个线程内的程序顺序兼容,即使是relaxed
  • 在线程看到一个加载的值后,它只能看到该值或修改顺序中较后的值,即使加载和存储是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()之间存在不同的happens-before链。因此,该存储在fetch_add之后、load之前对T2可见。

相关问题