简体   繁体   English

C++11 memory_order_acquire 和 memory_order_release 语义?

[英]C++11 memory_order_acquire and memory_order_release semantics?

http://en.cppreference.com/w/cpp/atomic/memory_order , and other C++11 online references, define memory_order_acquire and memory_order_release as: http://en.cppreference.com/w/cpp/atomic/memory_order和其他 C++11 在线参考资料,将 memory_order_acquire 和 memory_order_release 定义为:

  • Acquire operation: no reads in the current thread can be reordered before this load.获取操作:在本次加载之前,当前线程中没有读取可以重新排序。
  • Release operation: no writes in the current thread can be reordered after this store.释放操作:在此存储之后,当前线程中没有写入可以重新排序。

This seems to allow post-acquire-writes to be executed before the acquire operation, which seems weird too me (usual acquire/release operation semantics restrict movement of all memory operations).这似乎允许在获取操作之前执行获取后写入,这对我来说也很奇怪(通常获取/释放操作语义限制所有内存操作的移动)。

Same online source ( http://en.cppreference.com/w/cpp/atomic/atomic_flag ) suggests that a spinlock mutex can be built using C++ atomics and the above mentioned relaxed memory ordering rules:相同的在线资源( http://en.cppreference.com/w/cpp/atomic/atomic_flag )表明可以使用 C++ 原子和上述宽松的内存排序规则构建自旋锁互斥锁:

lock mutex: while (lock.test_and_set(std::memory_order_acquire))

unlock mutex: lock.clear(std::memory_order_release);               

With this definition of lock/unlock, wouldn't the simple code below be broken if memory_order_acquire/release are indeed defined this way (ie, not forbidding reordering of post-acquire-writes):有了这个锁定/解锁的定义,如果 memory_order_acquire/release 确实以这种方式定义(即,不禁止对 post-acquire-writes 重新排序),下面的简单代码不会被破坏:

Thread1:
  (0) lock
    (1) x = 1;
    (2) if (x != 1) PANIC
  (3) unlock

Thread2:
  (4) lock
    (5) x = 0;
  (6) unlock

Is the following execution possible: (0) lock, (1) x = 1, (5) x = 0, (2) PANIC ?以下执行是否可能:(0)锁定,(1)x = 1,(5)x = 0,(2)恐慌? What did I miss?我错过了什么?

The spinlock mutex implementation looks okay to me.自旋锁互斥体实现对我来说看起来不错。 I think they got the definitions of acquire and release completely wrong.我认为他们对获取释放的定义完全错误。

Here is the clearest explanation of acquire/release consistency models that I am aware of:Gharachorloo;这是我所知道的对获取/释放一致性模型的最清晰的解释:Gharachorloo; Lenoski; 莱诺斯基; Laudon; 劳登; Gibbons; 长臂猿; Gupta; 古普塔; Hennessy: Memory consistency and event ordering in scalable shared-memory multiprocessors, Int'l Symp Comp Arch , ISCA(17):15-26, 1990, doi 10.1145/325096.325102 . Hennessy:可扩展共享内存多处理器中的内存一致性和事件排序, Int'l Symp Comp Arch ,ISCA(17):15-26, 1990,doi 10.1145/325096.325102 (The doi is behind the ACM paywall. The actual link is to a copy not behind a paywall.) (doi 在 ACM 付费专区后面。实际链接是指向不在付费专区后面的副本。)

Look at Condition 3.1 in Section 3.3 and the accompanying Figure 3:查看第 3.3 节中的条件 3.1 和随附的图 3:

  • before an ordinary load or store access is allowed to perform with respect to any other processor, all previous acquire accesses must be performed, and在允许对任何其他处理器执行普通加载或存储访问之前,必须执行所有先前的获取访问,并且
  • before a release access is allowed to perform with respect to any other processor, all previous ordinary load and store accesses must be performed, and在允许对任何其他处理器执行释放访问之前,必须执行所有先前的普通加载和存储访问,并且
  • special accesses are [sequentially] consistent with respect to one another.特殊访问彼此[顺序]一致。

The point is this: acquires and releases are sequentially consistent 1 (all threads globally agree on the order in which acquires and releases happened.) All threads globally agree that the stuff that happens between an acquire and a release on a specific thread happened between the acquire and release.重点是:获取和释放是顺序一致的1 (所有线程全局同意获取和释放发生的顺序。)所有线程全局同意特定线程上的获取和释放之间发生的事情发生在获取和释放。 But normal loads and stores after a release are allowed to be moved (either by hardware or the compiler) above the release, and normal loads and stores before an acquire are allowed to be moved (either by hardware or the compiler) to after the acquire.但是允许发布之后(通过硬件或编译器)将正常加载和存储移动到发布上方,并且允许将获取之前的正常加载和存储移动(通过硬件或编译器)到获取之后.

(Footnote 1: This is true for most implementations, but an overstatement for ISO C++ in general. Reader threads are allowed to disagree about the order of 2 stores done by 2 other threads. See Acquire/release semantics with 4 threads , and this answer for details of how C++ compiled for POWER CPUs demonstrates the difference in practice with release and acquire, but not seq_cst. But most CPUs do only get data between cores via coherent cache that means a global order does exist.) (脚注 1:这对于大多数实现来说都是正确的,但通常对 ISO C++ 来说是夸大其词。允许读者线程不同意由 2 个其他线程完成的 2 个存储的顺序。请参阅4 个线程的获取/释放语义此答案有关为 POWER CPU 编译的 C++ 的详细信息,它展示了释放和获取在实践中的差异,而不是 seq_cst。但大多数 CPU 仅通过一致性缓存获取内核之间的数据,这意味着全局顺序确实存在。)


In the C++ standard (I used the link to the Jan 2012 draft) the relevant section is 1.10 (pages 11 through 14).C++ 标准中(我使用了 2012 年 1 月草案的链接),相关部分是 1.10(第 11 到 14 页)。

The definition of happens-before is intended to be modeled after Lamport;发生在之前的定义旨在模仿Lamport; Time, Clocks, and the Ordering of Events in a Distributed System, CACM , 21(7):558-565, Jul 1978 . 分布式系统中的时间、时钟和事件排序, CACM ,21(7):558-565,1978 年 7 月 C++ acquires correspond to Lamport's receives , C++ releases correspond to Lamport's sends . C++ acquiss对应Lamport的receives ,C++ releases对应Lamport的sends Lamport placed a total order on the sequence of events within a single thread, where C++ has to allow a partial order (see Section 1.9, Paragraphs 13-15, page 10 for the C++ definition of sequenced-before .) Still, the sequenced-before ordering is pretty much what you would expect.兰波特放置共订单上的事件的序列中的单个线程,其中C ++具有允许不完整阶内(参见第1.9节段13-15,第10页的C ++的测序-前定义)。尽管如此,sequenced-在订购之前几乎是您所期望的。 Statements are sequenced in the order they are given in the program.语句按照它们在程序中给出的顺序进行排序。 Section 1.9, paragraph 14: "Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated."第 1.9 节,第 14 段:“与完整表达式相关的每个值计算和副作用在与要评估的下一个完整表达式相关的每个值计算和副作用之前排序。”

The whole point of Section 1.10 is to say that a program that is data-race-free produces the same well defined value as if the program were run on a machine with a sequentially consistent memory and no compiler reordering.第 1.10 节的重点是说,一个无数据竞争的程序产生相同的明确定义的值,就好像程序在具有顺序一致内存且没有编译器重新排序的机器上运行一样。 If there is a data race then the program has no defined semantics at all.如果存在数据竞争,则程序根本没有定义的语义。 If there is no data race then the compiler (or machine) is permitted to reorder operations that don't contribute to the illusion of sequential consistency.如果没有数据竞争,则允许编译器(或机器)对不会造成顺序一致性错觉的操作重新排序。

Section 1.10, Paragraph 21 (page 14) says: A program is not data-race-free if there is a pair of accesses A and B from different threads to object X, at least one of those accesses has a side effect, and neither A happens-before B, nor B happens-before A. Otherwise the program is data-race-free.第 1.10 节第 21 段(第 14 页)说:如果从不同的线程对对象 X 进行了一对访问 A 和 B,那么程序不是无数据竞争的,其中至少有一个访问有副作用,并且两者都没有A 在 B 之前发生,B 在 A 之前发生。否则程序是无数据竞争的。

Paragraphs 6-20 give a very careful definition of the happens-before relation.第 6-20 段非常仔细地定义了happens-before 关系。 The key definition is Paragraph 12:关键定义是第 12 段:

"An evaluation A happens before an evaluation B if: “在以下情况下,评估A 发生在评估 B之前

  • A is sequenced before B, or A 在 B 之前排序,或
  • A inter-thread happens before B."线程间发生在 B 之前。”

So if an acquire is sequenced before (in the same thread) pretty much any other statement, then the acquire must appear to happen before that statement.因此,如果获取在几乎任何其他语句之前(在同一线程中)排序,那么获取必须出现在该语句之前。 (Including if that statement performs a write.) (包括该语句是否执行写入。)

Likewise: if pretty much any statement is sequenced before (in the same thread) a release, then that statement must appear to happen before the release.同样:如果几乎任何语句都在发布之前(在同一线程中)排序,那么该语句必须出现在发布之前。 (Including if that statement just does a value computation (read).) (包括该语句是否仅执行值计算(读取)。)

The reason that the compiler is allowed to move other computations from after a release to before a release (or from before an acquire to after an acquire) is because of the fact that those operations specifically do not have an inter-thread happens before relationship (because they are outside the critical section).编译器允许释放之前,释放后移至其他计算(或从acquire之前给acquire后)的原因是因为一个事实,即这些行动特别没有一个线程间的关系之前发生(因为它们在临界区之外)。 If they race the semantics are undefined, and if they don't race (because they aren't shared) then you can't tell exactly when they happened with regard to the synchronization.如果它们竞争语义是未定义的,并且如果它们不竞争(因为它们不共享),那么您无法准确判断它们何时发生在同步方面。

Which is a very long way of saying: cppreference.com's definitions of acquire and release are dead wrong.这是一个很长的说法:cppreference.com 对获取和释放的定义是完全错误的。 Your example program has no data race condition, and PANIC can not occur.您的示例程序没有数据竞争条件,并且不会发生 PANIC。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 理解 C++11 中的 `memory_order_acquire` 和 `memory_order_release` - Understanding `memory_order_acquire` and `memory_order_release` in C++11 与 memory_order_acquire 和 memory_order_release 的原子交换 - atomic exchange with memory_order_acquire and memory_order_release C++ 标准如何使用 memory_order_acquire 和 memory_order_release 防止自旋锁互斥锁中的死锁? - How C++ Standard prevents deadlock in spinlock mutex with memory_order_acquire and memory_order_release? 可以分别使用std :: memory_order_acquire和memory_order_release吗? - Can std::memory_order_acquire and memory_order_release be used separately? 什么时候可以从 compare_exchange 中安全删除 memory_order_acquire 或 memory_order_release? - When can memory_order_acquire or memory_order_release be safely removed from compare_exchange? C++ memory_order_acquire/release问题 - C++ memory_order_acquire/release questions MOV x86指令是否实现了C ++ 11 memory_order_release原子存储? - Does the MOV x86 instruction implement a C++11 memory_order_release atomic store? 为什么 c++ singleton 需要 memory_order_acquire - why c++ singleton need memory_order_acquire memory_order_consume 和 memory_order_acquire 的区别 - Difference between memory_order_consume and memory_order_acquire 为什么 memory_order_release 支持直到 C++20? - Why memory_order_release support until C++20?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM