C++ 每个 memory_order 是什么意思?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12346487/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What do each memory_order mean?
提问by Damon
I read a chapter and I didn't like it much. I'm still unclear what the differences is between each memory order. This is my current speculation which I understood after reading the much more simple http://en.cppreference.com/w/cpp/atomic/memory_order
我读了一章,我不太喜欢它。我仍然不清楚每个内存顺序之间的区别是什么。这是我目前的猜测,在阅读了更简单的http://en.cppreference.com/w/cpp/atomic/memory_order后,我明白了这一点
The below is wrong so don't try to learn from it
下面是错误的所以不要试图从中学习
- memory_order_relaxed: Does not sync but is not ignored when order is done from another mode in a different atomic var
- memory_order_consume: Syncs reading this atomic variable however It doesnt sync relaxed vars written before this. However if the thread uses var X when modifying Y (and releases it). Other threads consuming Y will see X released as well? I don't know if this means this thread pushes out changes of x (and obviously y)
- memory_order_acquire: Syncs reading this atomic variable AND makes sure relaxed vars written before this are synced as well. (does this mean all atomic variables on all threads are synced?)
- memory_order_release: Pushes the atomic store to other threads (but only if they read the var with consume/acquire)
- memory_order_acq_rel: For read/write ops. Does an acquire so you don't modify an old value and releases the changes.
- memory_order_seq_cst: The same thing as acquire release except it forces the updates to be seen in other threads (if
a
store with relaxed on another thread. I storeb
with seq_cst. A 3rd thread readinga
with relax will see changes along withb
and any other atomic variable?).
- 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
在另一个线程上使用relaxed 存储。我b
使用seq_cst存储。a
使用relax读取的第三个线程将看到更改以及b
任何其他原子变量? )。
I think I understood but correct me if i am wrong. I couldn't find anything that explains it in easy to read english.
我想我明白了,但如果我错了,请纠正我。我找不到任何用易于阅读的英语解释它的内容。
回答by Damon
The GCC Wiki gives a very thorough and easy to understand explanationwith code examples.
GCC Wiki 提供了非常透彻且易于理解的代码示例解释。
(excerpt edited, and emphasis added)
(编辑摘录,并添加重点)
IMPORTANT:
重要的:
Upon re-reading the below quote copied from the GCC Wiki in the process of adding my own wording to the answer, I noticed that the quote is actually wrong. They got acquireand consumeexactly the wrong way around. A release-consumeoperation only provides an ordering guarantee on dependent data whereas a release-acquireoperation provides that guarantee regardless of data being dependent on the atomic value or not.
在将我自己的措辞添加到答案的过程中,重新阅读从 GCC Wiki 复制的以下引用后,我注意到该引用实际上是错误的。他们以错误的方式获取和消费。甲释放消耗操作仅而提供相关的数据的排序保证释放获取无论操作提供数据依赖于所述原子值或不作出保证。
The first model is "sequentially consistent". This is the default mode used when none is specified, and it is the most restrictive. It can also be explicitly specified via
memory_order_seq_cst
. It provides the same restrictions and limitation to moving loads around that sequential programmers are inherently familiar with, except it applies across threads.
[...]
From a practical point of view, this amounts to all atomic operations acting as optimization barriers. It's OK to re-order things between atomic operations, but not across the operation. Thread local stuff is also unaffected since there is no visibility to other threads. [...] This mode also provides consistency across allthreads.The opposite approachis
memory_order_relaxed
. This model allows for much less synchronization by removing the happens-before restrictions. These types of atomic operations can also have various optimizations performed on them, such as dead store removal and commoning. [...] Without any happens-before edges, no thread can count on a specific ordering from another thread.
The relaxed mode is most commonly used when the programmer simply wants a variable to be atomic in naturerather than using it to synchronize threads for other shared memory data.The third mode (
memory_order_acquire
/memory_order_release
) is a hybridbetween the other two. The acquire/release mode is similar to the sequentially consistent mode, except it only applies a happens-before relationship to dependent variables. This allows for a relaxing of the synchronization required between independent reads of independent writes.
memory_order_consume
is a further subtle refinement in the release/acquire memory model that relaxes the requirements slightly by removing the happens before ordering on non-dependent shared variables as well.
[...]
The real difference boils down to how much state the hardware has to flush in order to synchronize. Since a consume operation maytherefore execute faster, someone who knows what they are doing can use it for performance critical applications.
第一个模型是“顺序一致的”。这是当没有指定时使用的默认模式,它是最严格的。也可以通过 明确指定
memory_order_seq_cst
。它提供了相同的限制和限制来移动负载,顺序程序员固有地熟悉,除了它适用于跨线程。
[...]
从实用的角度来看,这相当于所有原子操作都充当了优化障碍。在原子操作之间重新排序是可以的,但不能跨操作重新排序。线程本地内容也不受影响,因为其他线程不可见。[...] 这种模式还提供了所有线程的一致性。所述相反的方法是
memory_order_relaxed
。该模型通过消除发生之前的限制,允许更少的同步。这些类型的原子操作也可以对它们执行各种优化,例如删除死存储和共用。[...] 没有任何发生在边缘之前,任何线程都不能依赖另一个线程的特定顺序。当程序员只是希望一个变量本质上是原子的,而不是使用它来同步其他共享内存数据的线程时,最常使用
放松模式。第三种模式 (
memory_order_acquire
/memory_order_release
) 是其他两种模式的混合。获取/释放模式类似于顺序一致模式,不同之处在于它仅对因变量应用了先发生关系。这允许放宽独立写入的独立读取之间所需的同步。
memory_order_consume
是对释放/获取内存模型的进一步细微改进,它通过删除对非相关共享变量进行排序之前发生的情况,从而稍微放宽了要求。
[...]
真正的区别归结为硬件必须刷新多少状态才能同步。由于消费操作可能因此执行得更快,知道他们在做什么的人可以将其用于性能关键应用程序。
Here follows my own attempt at a more mundane explanation:
以下是我自己尝试更平凡的解释:
A different approach to look at it is to look at the problem from the point of view of reordering reads and writes, both atomic and ordinary:
看待它的另一种方法是从重新排序读取和写入(原子和普通)的角度来看问题:
Allatomic operations are guaranteed to be atomic within themselves (the combination of twoatomic operations is not atomic as a whole!) and to be visible in the total order in which they appear on the timeline of the execution stream. That means no atomic operation can, under any circumstances, be reordered, but other memory operations might very well be. Compilers (and CPUs) routinely do such reordering as an optimization.
It also means the compiler must use whatever instructions are necessary to guarantee that an atomic operation executing at any time will see the results of each and every other atomic operation, possibly on another processor core (but not necessarily other operations), that were executed before.
所有原子操作都保证在它们自己内部是原子的(两个原子操作的组合作为一个整体不是原子的!)并且在它们出现在执行流的时间线上的总顺序中可见。这意味着在任何情况下都不能对原子操作进行重新排序,但其他内存操作很可能会重新排序。编译器(和 CPU)经常将重新排序作为优化。
这也意味着编译器必须使用任何必要的指令来保证在任何时间执行的原子操作将看到每个其他原子操作的结果,可能在另一个处理器内核上(但不一定是其他操作),这些操作是之前执行的.
Now, a relaxedis just that, the bare minimum. It does nothing in addition and provides no other guarantees. It is the cheapest possible operation. For non-read-modify-write operations on strongly ordered processor architectures (e.g. x86/amd64) this boils down to a plain normal, ordinary move.
现在,放松就是这样,最低限度。它不做任何额外的事情,也不提供其他保证。这是最便宜的操作。对于强有序处理器架构(例如 x86/amd64)上的非读-修改-写操作,这归结为一个普通的普通移动。
The sequentially consistentoperation is the exact opposite, it enforces strict ordering not only for atomic operations, but also for other memory operations that happen before or after. Neither one can cross the barrier imposed by the atomic operation. Practically, this means lost optimization opportunities, and possibly fence instructions may have to be inserted. This is the most expensive model.
该顺序一致的操作是完全相反的,它强制执行严格的顺序不仅为原子操作,而且对于之前或之后发生的其他内存操作。任何人都无法跨越原子操作强加的障碍。实际上,这意味着失去优化机会,并且可能必须插入栅栏指令。这是最昂贵的模型。
A releaseoperation prevents ordinary loads and stores from being reordered afterthe atomic operation, whereas an acquireoperation prevents ordinary loads and stores from being reordered beforethe atomic operation. Everything else can still be moved around.
The combination of preventing stores being moved after, and loads being moved before the respective atomic operation makes sure that whatever the acquiring thread gets to see is consistent, with only a small amount of optimization opportunity lost.
One may think of that as something like a non-existent lock that is being released (by the writer) and acquired (by the reader). Except... there is no lock.
甲释放操作防止普通加载和存储被重新排序后的原子操作,而一个获取操作防止普通加载和存储被重新排序之前的原子操作。其他一切仍然可以移动。
防止在相应的原子操作之后移动存储和在相应原子操作之前移动负载的组合确保获取线程看到的任何内容都是一致的,仅损失少量优化机会。
人们可能会将其视为一种不存在的锁,它被(作者)释放并(被读者)获取。除了……没有锁。
In practice, release/acquire usually means the compiler needs not use any particularly expensive special instructions, but it cannotfreely reorder loads and stores to its liking, which may miss out some (small) optimization opportuntities.
在实践中,释放/获取通常意味着编译器不需要使用任何特别昂贵的特殊指令,但它不能根据自己的喜好自由地重新排序加载和存储,这可能会错过一些(小的)优化机会。
Finally, consumeis the same operation as acquire, only with the exception that the ordering guarantees only apply to dependent data. Dependent data would e.g. be data that is pointed-to by an atomically modified pointer.
Arguably, that may provide for a couple of optimization opportunities that are not present with acquire operations (since fewer data is subject to restrictions), however this happens at the expense of more complex and more error-prone code, and the non-trivial task of getting dependency chains correct.
最后,消耗与获取相同的操作,唯一的区别是排序保证仅适用于依赖数据。相关数据可以是例如由原子修改的指针指向的数据。
可以说,这可能提供了一些在获取操作中不存在的优化机会(因为较少的数据受到限制),但是这是以更复杂和更容易出错的代码为代价的,以及非平凡的任务获得正确的依赖链。
It is currently discouraged to use consumeordering while the specification is being revised.
当前不鼓励在修订规范时使用消费排序。
回答by Damon
This is a quite complex subject. Try to read http://en.cppreference.com/w/cpp/atomic/memory_orderseveral times, try to read other resources, etc.
这是一个相当复杂的课题。尝试多次阅读http://en.cppreference.com/w/cpp/atomic/memory_order,尝试阅读其他资源等。
Here's a simplified description:
这是一个简单的描述:
The compiler andCPU can reorder memory accesses. That is, they can happen in different order than what's specified in the code. That's fine most of the time, the problem arises when different thread try to communicate and may see such order of memory accesses that breaks the invariants of the code.
编译器和CPU 可以重新排序内存访问。也就是说,它们的发生顺序可能与代码中指定的顺序不同。大多数时候这很好,当不同的线程尝试通信时会出现问题,并且可能会看到这种破坏代码不变性的内存访问顺序。
Usually you can use locks for synchronization. The problem is that they're slow. Atomic operations are much faster, because the synchronization happens at CPU level (i.e. CPU ensures that no other thread, even on another CPU, modifies some variable, etc.).
通常您可以使用锁进行同步。问题是它们很慢。原子操作要快得多,因为同步发生在 CPU 级别(即 CPU 确保没有其他线程,即使在另一个 CPU 上,修改某些变量等)。
So, the one single problem we're facing is reordering of memory accesses. The memory_order
enum specifies what types of reorderings compiler mustforbid.
因此,我们面临的一个问题是内存访问的重新排序。该memory_order
枚举指定什么类型的重新排序的编译器必须禁止。
relaxed
- no constraints.
relaxed
- 没有限制。
consume
- no loads that are dependent on the newly loaded value can be reordered wrt. the atomic load. I.e. if they are after the atomic load in the source code, they will happenafter the atomic load too.
consume
- 依赖于新加载值的负载不能重新排序。原子负荷。即如果它们是在源代码中的原子加载之后,它们也会在原子加载之后发生。
acquire
- no loads can be reordered wrt. the atomic load. I.e. if they are after the atomic load in the source code, they will happenafter the atomic load too.
acquire
- 没有负载可以重新排序。原子负荷。即如果它们是在源代码中的原子加载之后,它们也会在原子加载之后发生。
release
- no stores can be reordered wrt. the atomic store. I.e. if they are before the atomic store in the source code, they will happenbefore the atomic store too.
release
- 没有商店可以重新订购。原子商店。即如果它们在源代码中的原子存储之前,它们也会发生在原子存储之前。
acq_rel
- acquire
and release
combined.
acq_rel
-acquire
并release
结合。
seq_cst
- it is more difficult to understand why this ordering is required. Basically, all other orderings only ensure that specific disallowed reorderings don't happen only for the threads that consume/release the same atomic variable. Memory accesses can still propagate to other threads in any order. This ordering ensures that this doesn't happen (thus sequential consistency). For a case where this is needed see the example at the end of the linked page.
seq_cst
- 更难理解为什么需要这种排序。基本上,所有其他排序只确保特定的不允许的重新排序不会只发生在消耗/释放相同原子变量的线程中。内存访问仍然可以以任何顺序传播到其他线程。这种排序确保不会发生这种情况(因此顺序一致性)。对于需要这样做的情况,请参见链接页面末尾的示例。