我读了一章,我不太喜欢它。我仍然不清楚每个内存顺序之间有什么区别。这是我目前的猜测,在阅读了更简单的 http://en.cppreference.com/w/cpp/atomic/memory_order 后我理解了
以下是错误的,所以不要试图从中学习
- memory_order_relaxed:不同步,但在不同原子变量中从另一个模式完成订单时不会被忽略
- memory_order_consume:同步读取这个原子变量,但是它不同步在此之前编写的宽松变量。但是,如果线程在修改 Y(并释放它)时使用 var X。其他消耗 Y 的线程也会看到 X 被释放?我不知道这是否意味着这个线程推出了 x(显然是 y)的变化
- memory_order_acquire:同步读取这个原子变量,并确保在此之前写入的宽松变量也被同步。 (这是否意味着所有线程上的所有原子变量都已同步?)
- memory_order_release:将原子存储推送到其他线程(但前提是它们使用消耗/获取读取 var)
- memory_order_acq_rel:用于读/写操作。是否进行获取,以便您不修改旧值并释放更改。
- memory_order_seq_cst:与获取释放相同,除了它强制在其他线程中看到更新(如果
a
在另一个线程上轻松存储。我存储b
与 seq_cst。第三个线程阅读a
放松会看到随着b
和任何其他原子变量的变化?)。
我想我理解但如果我错了请纠正我。我找不到任何用易于阅读的英语解释它的东西。
原文由 user34537 发布,翻译遵循 CC BY-SA 4.0 许可协议
GCC Wiki 通过代码示例提供了 非常全面且易于理解的解释。
(摘录已编辑,并添加了重点)
重要的:
在将我自己的措辞添加到答案的过程中重新阅读从 GCC Wiki 复制的以下引用时,我注意到引用实际上是错误的。他们以完全错误的方式 获取 和 _消费_。 release-consume 操作仅提供对依赖数据的排序保证,而 release-acquire 操作提供该保证,而不管数据是否依赖于原子值。
以下是我自己尝试的更平凡的解释:
另一种看待它的方法是从重新排序读取和写入的角度来看待问题,包括原子的和普通的:
所有 原子操作都保证在其自身内部是原子的( 两个 原子操作的组合不是一个整体的原子!)并且在它们出现在执行流的时间轴上的总顺序中是可见的。这意味着在任何情况下都不能对原子操作进行重新排序,但其他内存操作很可能会被重新排序。编译器(和 CPU)通常会进行这种重新排序作为优化。
这也意味着编译器必须使用任何必要的指令来保证在任何时候执行的原子操作将看到之前执行的每个其他原子操作的结果,可能在另一个处理器内核(但不一定是其他操作)上.
现在, 放松 就是这样,最低限度。除此之外,它什么也不做,也不提供任何其他保证。这是最便宜的手术。对于强排序处理器架构(例如 x86/amd64)上的非读-修改-写操作,这归结为一个普通的普通移动。
顺序一致 的操作正好相反,它不仅对原子操作强制执行严格的排序,而且对之前或之后发生的其他内存操作也强制执行严格的排序。任何人都无法跨越原子操作强加的障碍。实际上,这意味着失去优化机会,并且可能必须插入栅栏指令。这是最昂贵的型号。
释放 操作防止普通加载和存储在原子操作 之后 重新排序,而 获取 操作防止普通加载和存储在原子操作 之前 重新排序。其他一切仍然可以移动。
防止存储在之后移动以及在相应原子操作之前移动负载的组合确保了获取线程看到的任何内容都是一致的,只有少量的优化机会丢失。
人们可能会认为这就像一个不存在的锁,它正在被释放(由作者)和获取(由读者)。除了…没有锁。
在实践中,发布/获取通常意味着编译器不需要使用任何特别昂贵的特殊指令,但它 不能 随意重新排序加载和存储,这可能会错过一些(小)优化机会。
最后, consume 与 acquire 是相同的操作,只是排序保证仅适用于依赖数据。相关数据将是例如由原子修改的指针指向的数据。
可以说,这可能会提供一些获取操作不存在的优化机会(因为更少的数据受到限制),但是这是以更复杂和更容易出错的代码和非平凡任务为代价的获得正确的依赖链。
目前不鼓励在规范修订时使用 消费 排序。