gpt4 book ai didi

C++11 memory_order_acquire 和 memory_order_release 语义?

转载 作者:IT老高 更新时间:2023-10-28 12:58:05 25 4
gpt4 key购买 nike

http://en.cppreference.com/w/cpp/atomic/memory_order和其他 C++11 在线引用资料,将 memory_order_acquire 和 memory_order_release 定义为:

  • 获取操作:否阅读 在当前线程中可以在此加载之前重新排序。
  • 发布操作:否 写道 在当前线程中可以在此存储后重新排序。

  • 这似乎允许执行获取后写入 之前 获取操作,这对我来说也很奇怪(通常获取/释放操作语义限制 所有 内存操作的移动)。

    相同的在线资源( 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);

    有了这个锁定/解锁的定义,如果 memory_order_acquire/release 确实以这种方式定义(即,不禁止重新排序后获取写入),下面的简单代码不会被破坏:
    Thread1:
    (0) lock
    (1) x = 1;
    (2) if (x != 1) PANIC
    (3) unlock

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

    以下执行是否可能:(0)锁定,(1)x = 1,(5)x = 0,(2) panic ?我错过了什么?

    最佳答案

    自旋锁互斥体实现对我来说看起来不错。我认为他们对获取和释放的定义完全错误。
    以下是我所知道的对获取/释放一致性模型的最清晰的解释: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 . (doi 在 ACM 付费专区后面。实际链接是指向不在付费专区后面的拷贝。)
    查看第 3.3 节中的条件 3.1 和随附的图 3:

  • 在允许普通加载或存储访问之前
    相对于任何其他处理器执行,
    必须执行所有先前的获取访问,并且
  • 在释放访问被允许执行之前
    相对于任何其他处理器,所有以前的普通处理器
    必须执行加载和存储访问,并且
  • 特殊访问[顺序]与尊重一致
    彼此。

  • 重点是:获取和释放是顺序一致的1(所有线程全局同意获取和释放发生的顺序。)所有线程全局同意特定线程上的获取和释放之间发生的事情发生在获取和释放之间和释放。但是允许在发布之后(通过硬件或编译器)将正常加载和存储移动到发布上方,并且允许将获取之前的正常加载和存储移动(通过硬件或编译器)到获取之后.
    (脚注 1:这对于大多数实现来说都是正确的,但对 ISO C++ 来说通常是夸大其词。读者线程可以不同意由 2 个其他线程完成的 2 个存储的顺序。有关详细信息,请参阅 Acquire/release semantics with 4 threadsthis answer C++ 是如何为 POWER CPU 编译的,它展示了释放和获取在实践中的差异,而不是 seq_cst。但大多数 CPU 只通过一致的缓存在内核之间获取数据,这意味着全局顺序确实存在。)

    C++ standard (我使用了 2012 年 1 月草案的链接)相关部分是 1.10(第 11 到 14 页)。
    发生之前的定义旨在模仿 Lamport; Time, Clocks, and the Ordering of Events in a Distributed System, CACM, 21(7):558-565, Jul 1978 . C++获取对应Lamport的接收,C++释放对应Lamport的发送。 Lamport 在单个线程内的事件序列上放置了一个全序,其中 C++ 必须允许偏序(请参阅第 1.9 节,第 13-15 段,第 10 页,了解sequenced-before 的 C++ 定义)。在订购之前几乎是您所期望的。语句按它们在程序中给出的顺序排列。第 1.9 节第 14 段:“与完整表达式相关的每个值计算和副作用都排在每个值之前
    与要评估的下一个完整表达式相关的计算和副作用。”
    第 1.10 节的重点是说,一个无数据竞争的程序产生相同的明确定义的值,就好像程序在具有顺序一致内存且没有编译器重新排序的机器上运行一样。如果存在数据竞争,则程序根本没有定义的语义。如果没有数据竞争,则允许编译器(或机器)对不会造成顺序一致性错觉的操作重新排序。
    第 1.10 节第 21 段(第 14 页)说:如果从不同的线程对对象 X 进行了一对访问 A 和 B,那么程序不是无数据竞争的,其中至少有一个访问有副作用,并且两者都没有A 在 B 之前发生,B 在 A 之前发生。否则程序是无数据竞争的。
    第 6-20 段非常仔细地定义了happens-before 关系。关键定义是第 12 段:
    “在以下情况下,评估 A 发生在评估 B 之前:
  • A 在 B 之前排序,或
  • 线程间发生在 B 之前。”

  • 因此,如果获取在几乎任何其他语句之前(在同一线程中)排序,那么获取必须出现在该语句之前。 (包括该语句是否执行写入。)
    同样:如果几乎任何语句都在发布之前(在同一线程中)排序,那么该语句必须出现在发布之前。 (包括该语句是否仅执行值计算(读取)。)
    允许编译器将其他计算从发布之后移动到发布之前(或从获取之前到获取之后)的原因是因为这些操作特别没有在关系之前发生的线程间(因为它们在临界区之外)。如果它们竞争语义是未定义的,并且如果它们不竞争(因为它们不共享),那么您无法准确判断它们何时发生在同步方面。
    这是一个很长的说法:cppreference.com 对获取和释放的定义是完全错误的。您的示例程序没有数据竞争条件,并且不会发生 PANIC。

    关于C++11 memory_order_acquire 和 memory_order_release 语义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16179938/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com