Memory 在C++11中,对原子变量的读取是否保证获得它的当前值?

Memory 在C++11中,对原子变量的读取是否保证获得它的当前值?,memory,c++11,synchronization,atomic,Memory,C++11,Synchronization,Atomic,众所周知,对单个原子变量的修改形成一个总的顺序。假设我们在挂钟时间T对某个原子变量v进行了原子读取操作。那么,该读取是否保证获得在时间T按v的修改顺序由最后一个变量写入的v的当前值?换句话说,如果原子写入是在自然时间内原子读取之前完成的,并且其间没有其他写入,那么读取是否保证返回刚刚写入的值 我接受的答案是库比对他的答案的第六条评论。挂钟时间无关紧要。但是,您所描述的内容听起来像是写-读一致性保证: $1.10[简介.多线程]/20 如果原子对象M上的副作用X发生在M的值计算B之前,则评估B应从

众所周知,对单个原子变量的修改形成一个总的顺序。假设我们在挂钟时间T对某个原子变量v进行了原子读取操作。那么,该读取是否保证获得在时间T按v的修改顺序由最后一个变量写入的v的当前值?换句话说,如果原子写入是在自然时间内原子读取之前完成的,并且其间没有其他写入,那么读取是否保证返回刚刚写入的值


我接受的答案是库比对他的答案的第六条评论。

挂钟时间无关紧要。但是,您所描述的内容听起来像是写-读一致性保证:

$1.10[简介.多线程]/20

如果原子对象M上的副作用X发生在M的值计算B之前,则评估B应从X或按照M的修改顺序从X之后的副作用Y中获取其值

翻译标准英语时,值计算是一种读取,而副作用是一种写入


特别是,如果您的relaxed write和relaxed read位于同一函数的不同语句中,则它们通过sequenced before关系连接,因此它们通过“发生在之前”关系连接,因此保证有效。

取决于您可以为加载操作指定的内存顺序

默认情况下,它是std::memory\u order\u seq\u cst,答案是yes,如果存储了,它保证由另一个线程存储的当前值,即它必须至少使用std::memory\u order\u release memory order,否则无法保证存储可见性

但是如果为加载操作指定std::memory\u order\u relaxed,则表示relaxed排序:没有同步或排序约束,此操作只需要原子性。也就是说,程序最终可能根本无法从内存中读取数据

对原子变量的读取是否保证获取其当前值

没有

即使每个原子变量都有一个由所有线程观察的修改顺序,但这并不意味着所有线程都在同一时间尺度上观察修改

考虑以下代码:

std::atomic<int> g{0};

// thread 1
g.store(42);

// thread 2
int a = g.load();
// do stuff with a
int b = g.load();
可能的结果如图所示:

线程1:42存储在时间T1 线程2:第一次加载在时间T2返回0 线程2:线程1中的存储在时间T3变为可见 线程2:第二次加载在时间T4返回42。 即使在时钟时间T1存储之后发生时间T2的第一次加载,该结果也是可能的

标准上说:

实现应该使原子存储在合理的时间内对原子负载可见

它不要求存储立即可见,甚至允许存储保持不可见的空间,例如在没有缓存一致性的系统上。 在这种情况下,需要一个原子读-修改-写RMW来访问最后一个值

原子读修改写操作应始终读取写入的修改顺序中的最后一个值 与读修改写操作关联的写操作之前

不用说,RMW的执行成本更高——它们锁定总线,这就是为什么允许常规原子加载返回旧的缓存值。
如果需要定期加载才能返回最后一个值,那么性能将非常糟糕,而几乎没有任何好处。

这不是原子化的全部意义吗?在研究了内存顺序可能涉及的复杂性之后,现在对我来说似乎不太明显。@stijn不是平行MT的全部点没有全球同步时间吗?@curiousguy我不想说“全部”点,但OP使用墙时间的概念只是为了表达之前的概念/after@stijnBefore/after仅在给定线程上完全定义,并在该线程中发出信号。在线程之间有不同的非常抽象的顺序,它们必须与线程匹配,有时还必须与其他线程匹配……我的问题在于,当值计算读取写入的值时,写入线程之间的顺序发生在读取之前。但有没有任何条件,如果满足了,这种情况实际上会发生,否则,这种情况可能永远不会发生?我在考虑是否以挂钟时间的形式出现在之前就是这样一个条件。必要的条件是“发生在之前”的定义。它不涉及挂钟。我想下面的例子可以清楚地说明我的问题。考虑两个由原子自旋锁同步的线程。线程A首先获取锁。线程B正忙着等待。接下来,线程A释放锁。有没有线索表明线程B何时看到线程A的这个释放,从而进入临界区,或者它可能只是在无限长的等待中看到这个释放?@Lingxi,线索是内存顺序语义。A中的存储必须是内存\u顺序\u释放至少,负载必须为t
按顺序_acquire@Lingxi29.3[atomics.order]/12实现应该在合理的时间内使原子存储对原子负载可见。我认为内存顺序cst实际上不能保证这一点。它确实保证了一个全局总顺序的存在,但没有明确说明这个顺序是如何形成的,即构造这样一个顺序的规则。这些规则是针对内存顺序描述的。实现机制是防止在编译端和处理器端的原子加载或存储之前和之后省略和重新排序操作。因此,在其他操作中,保证存储发生在正确的位置,负载发生在正确的位置;处理器将执行这些操作。@Lingxi Consistency意味着从seq_cst到Released的任何原子操作的顺序都必须遵循给定线程中的执行顺序,您不能及时返回;通过多个线程获取和释放相同的对象将操作顺序联系在一起,并强制它们继续。如果线程之间没有共享原子对象,那么就没有这种联系。但那你为什么要关心抽象顺序呢?它确实保证了一个全局总顺序存在于不相关线程中的操作全局顺序,这些线程不共享任何东西,这是一个抽象的智能设备,没有物理对应物。您必须考虑通过原子对象进行交互的线程:在实践中,这些将以一些顺序可见的栅栏来实现,并且原子存储被实现为真正的存储器存储,它们将高速缓存行置于专用的修改状态,该状态确定高速缓存行中的对象的修改顺序。围栏使这些修改全局一致。在哪种实际情况下,常规加载可以返回旧的过时值?那个旧值存储在哪里?@curiousguy在缓存中,如果不一致的话。。如果存储的其他核心仍在存储缓冲器x86中,也可以考虑负载过时。问题是,对于常规存储和加载,并没有当前或最新值的定义。修改命令中最新的一个只适用于RMW操作,其中现代形式的C++和C++编译器有一个非相干缓存。@好奇怪,没有概念,但是缓存一致性不是强制性的特征,在我看来,一致性对于支持任何公共PL都是必要的,并且效率合理。在理论系统W/O缓存一致性上是否至少有C++编译器原型?