C++11内存_顺序_获取和内存_顺序_释放语义?

2024-04-19

http://en.cppreference.com/w/cpp/atomic/memory_order http://en.cppreference.com/w/cpp/atomic/memory_order以及其他 C++11 在线参考,将 memory_order_acquire 和 memory_order_release 定义为:

  • 获取操作:无reads在当前线程中可以在此加载之前重新排序。
  • 释放操作:无writes在当前线程中可以在此存储之后重新排序。

这似乎允许执行获取后写入before获取操作,这对我来说似乎很奇怪(通常获取/释放操作语义限制all内存操作)。

相同的在线来源(http://en.cppreference.com/w/cpp/atomic/atomic_flag 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 ?我错过了什么?


自旋锁互斥体的实现对我来说看起来不错。我认为他们得到了定义acquire and release完全错误的。

这是我所知道的获取/释放一致性模型的最清晰的解释:加拉乔卢;莱诺斯基;劳登;长臂猿;古普塔; Hennessy:可扩展共享内存多处理器中的内存一致性和事件排序,国际症状比较拱门, ISCA(17):15-26, 1990, doi 10.1145/325096.325102 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.8112。 (doi 位于 ACM 付费墙后面。实际链接是副本not在付费专区后面。)

查看第 3.3 节中的条件 3.1 和附图 3:

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

The point is this: acquires and releases are sequentially consistent1 (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. 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.

(脚注 1:这适用于most实现,但对于 ISO C++ 总体来说有点夸大其词。读者线程可以对另外 2 个线程完成的 2 个存储的顺序有不同意见。看使用 4 个线程获取/释放语义 https://stackoverflow.com/questions/48383867/acquire-release-semantics-with-4-threads, and 这个答案 https://stackoverflow.com/questions/27807118/will-two-atomic-writes-to-different-locations-in-different-threads-always-be-see/50679223#50679223有关如何为 POWER CPU 编译 C++ 的详细信息,演示了释放和获取在实践中的差异,但 seq_cst 没有。但大多数 CPU 仅通过一致性缓存在内核之间获取数据,这意味着全局顺序确实存在。)


In the C++标准 http://isocpp.org/std/the-standard(我使用了 2012 年 1 月草案的链接)相关部分是 1.10(第 11 页到第 14 页)。

的定义发生在之前旨在模仿兰波特;分布式系统中的时间、时钟和事件顺序,CACM,21(7):558-565,1978 年 7 月 http://research.microsoft.com/en-us/um/people/lamport/pubs/time-clocks.pdf. C++ acquires对应兰波特的receives, C++ releases对应兰波特的sends。 Lamport 对单个线程内的事件序列进行了总排序,其中 C++ 必须允许部分排序(请参阅第 1.9 节,第 13-15 段,第 10 页,了解 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 之前排序,或者
  • A 线程间发生在 B 之前。”

所以如果一个获取是之前测序(在同一线程中)几乎任何其他语句,那么获取必须出现在该语句之前。 (包括该语句是否执行写入。)

同样:如果几乎任何陈述都是之前测序(在同一个线程中)发布,那么该语句必须出现在发布之前。 (包括该语句是否只进行值计算(读取)。)

编译器的原因is允许将其他计算从释放之后移动到释放之前(或从获取之前移动到获取之后)是因为这些操作专门执行not在关系之前发生线程间(因为它们位于临界区之外)。如果它们竞争,则语义是未定义的,如果它们不竞争(因为它们不共享),那么您无法准确判断它们何时发生同步。

这是一个很长的说法:cppreference.com 对获取和释放的定义是完全错误的。您的示例程序没有数据竞争条件,并且不会发生 PANIC。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11内存_顺序_获取和内存_顺序_释放语义? 的相关文章

  • 使用 Unity 跨多种类型注入相同的 DataContext 实例

    假设我有 IRepository 接口及其实现 SqlRepository 它将 LINQ to SQL DataContext 作为参数 假设我有 IService 接口及其实现服务 它需要三个 IRepository IReposito
  • Windows Phone 8.1 中的 RadialGradientBrush?

    请告诉我如何在 Windows Phone 8 1 应用程序中创建 RadialGradientBrush 我试图在这里找到它 但没有这样的刷子 Windows UI Xaml Media 有任何想法吗 谢谢你 如果您不介意从 NuGet
  • 如何在 C# 2.0 中重写 lambda 表达式?

    MatchEvaluator evaluator match gt var splitPos match Value IndexOf var newValue match Value Substring 0 splitPos 2 Retri
  • 实体框架迁移 - 启用自动迁移以及添加的迁移

    我在我的项目中使用实体框架 4 3 迁移 我想使用自动迁移 以便当我对域对象和上下文类进行修改时 我的数据库会在运行项目时自动更新 到目前为止我已经完成了这个工作 除了自动迁移之外 我还想使用一些添加的迁移 并且我希望应用程序在运行应用程序
  • HtmlAgilityPack 有属性吗?

    我想做的就是 node Attributes class Value 但如果节点没有class属性 就崩溃了 所以 我必须先检查它是否存在 对吧 我怎么做 Attributes不是一个字典 它是一个包含内部字典的列表 并且没有 HasAtt
  • 以有效的方式找到最近点

    我在 2d 平面上有一个点 例如 x0 y0 和一组 n 点 x1 y1 xn yn 我想在 a 中找到距离 x0 y0 最近的点比尝试所有要点要好得多 有什么解决办法吗 我还应该说我的观点是这样排序的 bool less point a
  • 如何在 C++ 中将方法结果作为参数传递给基类构造函数?

    我试图实现这样的目标 class Base public Base string S class Derived Base public int foo string bar return stringof foo actually som
  • 在锁定屏幕上显示 UWP 控件

    我做了什么 我得到的要求是用户需要能够通过 Windows 锁定屏幕启动我的应用程序 搜索 www 后发现 从锁定屏幕连接热键非常困难 如果不是不可能的话 然后我发现这个帖子 https stackoverflow com question
  • Task.Run 如何受 CPU 内核限制?

    为什么下面的程序只会运行有限数量的阻塞任 务 限制数量似乎是机器上的核心数量 最初 当我写这篇文章时 我希望看到以下内容 作业 1 24 的作业完成输出 2秒的间隙 工作产出 25 48 然而输出是 作业 1 4 的作业完成输出 然后每隔
  • C:epoll和多线程

    我需要创建专门的 HTTP 服务器 为此我计划使用 epoll sycall 但我想利用多个处理器 核心 但我无法提出架构解决方案 ATM我的想法如下 使用自己的epoll描述符创建多个线程 主线程接受连接并将它们分配给线程epoll 但还
  • 在 C# 中快速加载/读取 TIFF 文件

    我正在编写一个处理 TIFF 图像的 C 应用程序 主要是显示文件 重新排序页面 删除页面 分割多页图像 将单个图像合并为一个多页图像等 我们处理的大多数图像都较小 无论是文件大小还是页码 但也有一些较大的图像 显示图像时 我们需要将多页
  • 为什么宇宙飞船与盒子对撞机相撞后不再回头?

    我希望当发生碰撞时将宇宙飞船转回来 但他们继续前进 开箱即用的对撞机和地形 使克隆船在碰撞时我想返回的脚本 using System using UnityEngine using Random UnityEngine Random usi
  • C# 中的私有“集合” - 无法理解它

    我见过很多使用类似的东西编写的示例代码 请原谅这是多么可怕的罐头 public class Test public object Thingy get private set 不幸的是 这些例子从未真正解释为什么 set 被设置为私有 所以
  • 使用 For 循环进行异步和等待 [重复]

    这个问题在这里已经有答案了 我有一个 Windows 服务 它根据计划运行各种作业 确定要运行哪些作业后 将调度对象列表发送到迭代列表并运行每个作业的方法 问题是 由于外部数据库调用 某些作业可能需要长达 10 分钟才能运行 我的目标是不让
  • C++ 将浮点数转换为无符号字符?

    我是 C 新手 我想做了一些谷歌搜索sprintf可以完成这项工作 但是编译时出现错误 无法在unsigned char and a char 我需要一个无符号字符 因为我要打印到图像文件 0 255 RGB unsigned char p
  • 以系统帐户运行exe

    我正在尝试将我的 c exe 作为系统帐户运行 我怎样才能做到这一点 我试过了
  • 使用实体框架而不使用 using 语句的缺点?

    有很多这样的代码块 public class SomeController Controller DbEntities entity new DbEntities public ActionResult Add entity someOpe
  • 在 C++ 中是否有一种简单的方法可以将由空格字符分隔的一行输入拆分为整数?

    我是一名 C 初学者 我一直在开发一个项目 在该项目中 您必须输入一些用空格分隔的整数 并且程序必须输出所有可能的整数排列 我知道在 python 中 这可以使用 int item for item in input split 但我不知道
  • 我应该复制 std::function 还是可以始终引用它?

    在我的 C 应用程序 使用 Visual Studio 2010 中 我需要存储 std function 如下所示 class MyClass public typedef std function
  • Selenium 3.7 和 Firefox ESR 52.4.1 是否需要 geckodriver?

    我的理解是 当使用Selenium WebDriver来自 NuGet 的 v3 7 我需要当前版本的 geckodriver 才能与 Firefox ESR v52 4 1 交互 但是 我已经成功运行测试并成功通过withoutgecko

随机推荐