读取原子修改的值是否需要内存屏障?

2023-12-04

鉴于以下情况:

class Foo
{
public:
    void Increment()
    {
        _InterlockedIncrement(&m_value); // OSIncrementAtomic
    }

    long GetValue()
    {
        return m_value;
    }

private:
    long m_value;
};

读取时是否需要内存屏障m_value?我的理解是_InterlockedIncrement将生成完整的内存屏障,并确保在发生任何后续加载之前存储该值。所以从这方面来说这听起来很安全,但是,可以m_value完全被缓存,即可以GetValue()即使以原子方式递增,也会返回过时的值?

Jeff Preshing 的优秀文章可供参考:https://preshing.com/20120515/memory-reordering-caught-in-the-act/

附加背景:我正在关注一系列有关无锁编程的文章,特别是查看unfinishedJobs变量和潜在的实施HasJobCompleted here: https://blog.molecular-matters.com/2015/08/24/job-system-2-0-lock-free-work-stealing-part-1-basics/

void Wait(const Job* job)
{
  // wait until the job has completed. in the meantime, work on any other job.
  while (!HasJobCompleted(job))
  {
    Job* nextJob = GetJob();
    if (nextJob)
    {
      Execute(nextJob);
    }
  }
}

可以通过将 unfinishedJobs 与 0 进行比较来确定作业是否已完成。

因此,考虑到这种情况,可能会实施HasJobCompleted需要内存屏障吗?


不,您不需要屏障,但如果读者和编写者在不同的线程中调用这些函数,您的代码无论如何都会被破坏。特别是当读者在循环中调用 read 函数时。

TL:DR:使用 C++11std::atomic<long> m_value with return m_value++在增量和return m_value在读者中。这将为您提供无数据争用程序中的顺序一致性:执行将像线程以源顺序的某种交错运行一样工作。 (除非您违反规则并有其他非atomic共享数据。)你肯定想返回一个值Increment,如果您希望线程执行增量操作以了解它们产生的值。对于像这样的用例完全损坏后执行单独的加载int sequence_num = shared_counter++;其中另一个线程的增量可以在之间可见count++; tmp = count;.

如果您不需要关于操作的如此强的排序other与读取器/写入器位于同一线程中的对象,return m_value.load(std::memory_order_acquire)足以满足大多数用途,并且m_value.fetch_add(1, std::memory_order_acq_rel)。实际上很少有程序在任何地方都需要 StoreLoad 屏障;即使使用原子 RMW 实际上也不能重新排序acq_rel。 (在 x86 上,它们的编译方式与您使用的相同seq_cst.)

你不能在线程之间强制排序;负载要么看到该值,要么看不到该值,具体取决于读取线程在获取/尝试获取负载值之前或之后是否看到来自编写器的无效。线程的全部意义在于它们don't彼此步调一致地运行。


数据竞赛 UB:

循环阅读m_value可以将负载提升出循环,因为它不是atomic(甚至volatile作为黑客)。这是数据竞争 UB,编译器会破坏你的阅读器。看this and 多线程程序卡在优化模式但在-O0下正常运行

障碍不是这里的问题/解决方案,只是强制重新检查内存(或当前 CPU 看到的内存的缓存一致性视图;L1d 和 L2 等实际的 CPU 缓存对此不是问题)。这并不是障碍真正的作用。他们命令该线程访问一致缓存。 C++ 线程仅跨具有一致缓存的核心运行。

但说真的,不要在没有very有说服力的理由。何时在多线程中使用 易失性?几乎从来没有。该答案解释了缓存一致性,并且您不需要障碍来避免看到过时的值。

在许多现实世界的 C++ 实现中,类似std::atomic_thread_fence()也将是一个“编译器屏障”,强制编译器从内存中重新加载非原子变量,即使没有volatile,但这是一个实现细节。因此,在某些 ISA 的某些编译器上,它可能运行得足够好。并且仍然不能完全安全地防止编译器发明多个加载;请参阅 LWN 文章谁害怕一个糟糕的优化编译器?有关详细信息的示例;主要针对 Linux 内核如何滚动自己的原子volatile,事实上它是由 GCC/clang 支持的。


“最新值”

初学者经常对此感到恐慌,并认为 RMW 操作由于其指定方式而在某种程度上更好。因为它们是捆绑在一起的读+写,并且每个内存位置都有一个修改顺序分别地,RMW 操作必然必须等待对缓存行的写访问,这意味着在单个位置上串行化所有写入和 RMW。

原子变量的普通加载仍然保证(通过实际实现)立即看到值。 (ISO C++ 仅建议值should可以在有限的时间内迅速看到,但当然实际的实现可以做得更好,因为它们运行在缓存一致的 CPU 硬件上。)

两个线程之间不存在“立即”这样的东西;另一个线程中的加载看到存储的值,或者它在存储对其他线程可见之前运行但不可见。通过线程调度等,线程总是可能会加载一个值,但很长一段时间不会使用它;装载时它是新鲜的。

因此,这与正确性几乎无关,剩下的就是担心线程间延迟。在某些情况下,这可能会受到屏障的帮助(以减少后续内存操作的争用,not更快地主动清理你的商店,障碍只是等待以正常方式发生)。因此,这通常是一个非常小的影响,而不是使用额外障碍的理由。

See MESI 协议和 std::atomic - 它是否确保所有写入对其他线程立即可见?。并查看我的评论https://github.com/dotnet/runtime/issues/67330#issuecomment-1083539281 and 除了提供必要的保证之外,硬件内存屏障是否还能使原子操作的可见性更快? 常常没有,即使是这样,也不会相差太多。

当然,这不足以值得用大量额外的障碍来减慢读者的速度,只是为了让它看看这个atomic变量晚于其他变量atomic变量,如果您不需要该顺序来保证正确性。或者放慢作家的速度,让它坐在那里什么都不做maybe让它更快地完成 RFO 几个周期,而不是完成其他有用的工作。

如果您对线程的使用在核心间延迟方面遇到了如此严重的瓶颈,以至于这是值得的,那么这可能表明您需要重新考虑您的设计。

没有障碍或顺序,只需std::atomic with memory_order_relaxed,您通常仍然会在大约 40 纳秒内看到其他内核上的数据(在现代 x86 台式机/笔记本电脑上),大约与两个线程都使用原子 RMW 相同。而且它不可能被延迟任何显着的时间,比如一微秒,如果你为许多早期的存储创建了大量的争用,那么它们都需要很长时间才能在这个存储之前提交。您绝对不必担心走很长时间看不到商店。 (当然这仅适用于atomic或手卷原子volatile。普通的非易失性负载只能在循环开始时检查一次,然后就不再检查了。这就是为什么它们无法用于多线程。)

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

读取原子修改的值是否需要内存屏障? 的相关文章

  • C# 方法重载决策不选择具体的泛型覆盖

    这个完整的 C 程序说明了这个问题 public abstract class Executor
  • Grpc - 将消息从一个客户端发送到连接到同一服务器的另一个客户端

    是否可以将消息从一个客户端发送到连接到同一服务器的另一个客户端 我想将数据从一个客户端发送到服务器然后发送到特定客户端 我想我需要获取客户端 ID 但我不知道如何获取此 ID 以及如何从服务器将此消息发送到该客户端 我这里有一个样本 这是一
  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • 现代 C++ 编译器是否能够在某些情况下避免调用 const 函数两次?

    例如 如果我有以下代码 class SomeDataProcessor public bool calc const SomeData d1 const SomeData d2 const private Some non mutable
  • 无法注册时间触发的后台任务

    对于 Windows 8 应用程序 在 C Xaml 中 我尝试注册后台任务 很难说 但我想我的后台任务已正确注册 但是当我单击调试位置工具栏上的后台任务名称时 我的应用程序停止工作 没有任何消息 我查看了事件查看器上的日志 得到 具有入口
  • 在 C# 中,如何根据在 gridview 行中单击的按钮引用特定产品记录

    我有一个显示产品网格视图的页面 该表内有一列 其中有一个名为 详细信息 的超链接 我想这样做 以便如果用户单击该特定产品的详细信息单元格 将打开一个新页面 提供有关该产品的更多信息 我不确定如何确定哪个Product记录链接的详细信息以及我
  • 如何使用 x64 运行 cl?

    我遇到了和这里同样的问题致命错误 C1034 windows h 未设置包含路径 https stackoverflow com questions 931652 fatal error c1034 windows h no include
  • 如何递归取消引用指针(C++03)?

    我正在尝试在 C 中递归地取消引用指针 如果传递一个对象 那就是not一个指针 这包括智能指针 我只想返回对象本身 如果可能的话通过引用返回 我有这个代码 template
  • 如果输入被重定向则执行操作

    我想知道如果我的输入被重定向 我应该如何在 C 程序中执行操作 例如 假设我有已编译的程序 prog 并且我将输入 input txt 重定向到它 我这样做 prog lt input txt 我如何在代码中检测到这一点 一般来说 您无法判
  • 在 C# 中为父窗体中的子窗体控件添加事件处理程序

    我有两种形式 一种是带有按钮和文本框的父表单 单击该按钮时 将打开一个对话框 该子窗体又包含一个文本框和一个按钮 现在我想要的是 每当子表单文本框中的文本更改时 父表单文本框中的文本会自动更改 为了获得这个 我所做的是 Form3 f3 n
  • 模板类的模板构造函数的 C++ 显式模板特化

    我有一个像这样的课程 template
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 使动态创建的链接标签在 Winforms 中可点击

    我正在制作一个程序 允许用户单击由动态链接标签创建的公司名称 在我想知道如何做到这一点之前 我从未在 C 中使用过链接标签 可为特定用户生成的业务数量各不相同 因此每个用户的链接标签数量并不相同 然后我想捕获业务 ID 以进行 Json 调
  • 如何解压 msgpack 文件?

    我正在将 msgpack 编码的数据写入文件 在编写时 我只是使用 C API 的 fbuffer 如 我为示例删除了所有错误处理 FILE fp fopen filename ab msgpack packer pk msgpack pa
  • WPF DataGrid / ListView 绑定到数组 mvvm

    我们假设你有 N 个整数的数组 表示行数的整数值 在模型中 该整数绑定到视图中的 ComboBox Q1 如何将数组 或数组的各个项目 绑定到 DataGrid 或 ListView 控件 以便 当您更改 ComboBox 值时 只有那么多
  • 了解 Lambda 表达式和委托 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经尝试解决这个问题很长一段时间了 阅读在线博客和文章 但到目前为止还没有成功 什么是代表 什么是 Lambda 表达式 两者的优点
  • EntityFramework 6.0.0.0 读取数据,但不插入

    我创建了一个基于服务的数据库 folderName gt Add New Item gt Data gt Service based Database文件到 WPF 应用程序中 然后我用过Database First方法并创建了Person
  • 是否允许全局静态标识符以单个 _ 开头?

    换句话说 可能static 文件范围 全局变量恰好以一个下划线开头 而不会产生与 C 实现发生名称冲突的可能性 https www gnu org software libc manual html node Reserved Names
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的

随机推荐

  • 无法绘制 MKPolylineView 交叉经度 +/-180

    我在 MKMapView 上绘制 MKPolylineView 时遇到问题 该线路代表一次环游世界的旅行 起点和终点均在纽约附近 始终向东行驶 从日本到旧金山的旅程的其中一段跨越太平洋 因此经度为 180 MKPolylineView 确实
  • 如何获取 WFFM 字段的值作为标签并将其输出到 Sitecore DMS 报告中?

    如果我创建一个启用了分析的 Web Forms For Marketers 表单 我可以选择将每个字段作为标签添加到访客 我看不到如何配置应将它们添加到哪个标签 甚至看不到默认情况下该标签的名称 我假设创建了带有字段名称的标签 我还想知道如
  • OmniAuth 无效响应错误

    我将 OmniAuth 与 Devise 结合使用 允许用户使用 Facebook 登录或使用用户名和密码创建普通帐户 当我最初设置这一切时 我使用了来自铁路广播 两个多月以来 一切都运行良好 但就在前几天 Facebook 登录停止工作
  • 如何在同一主机上运行 Angular 2 客户端应用程序和 Node 服务器应用程序

    我在 Angular 2 中构建了一个应用程序来从数据库获取数据 并使用 node express 从服务器获取数据并将其提供给 Angular 客户端 目前它们都运行在不同的本地主机上 如何将它们组合成一个项目并在同一主机上运行 假设您的
  • Android 管理 API:企业/策略列表?

    这让我抓狂 我已成功遵循Android 管理 API 快速入门创建项目 企业 策略并将其安装在设备上 我愚蠢地没有写下企业或策略 ID 我尝试创建一个新集 但非企业电子邮件现在给出错误 表明它已经是另一个 EMM 的一部分 控制台中是否有一
  • Heroku SSL 错误:密钥与 PEM 证书不匹配

    我正在尝试使用 Heroku 建议的说明生成自签名证书 http www akadia com services ssh test certificate html 这将创建 sever key 和 server crt 然后我尝试使用以下
  • 隐藏 UITableViewCell

    有没有办法隐藏 UITableView 单元格 我正在寻找一些可以在同步 cellForRowAtIndexPath 返回的 UITableViewCell 上调用的属性或方法 以隐藏它并使其无法被用户选择 对我来说 使用映射并不是一个简单
  • 如何获取并解析附加到 url 中的查询字符串? PHP

    我正在尝试开发一个 PHP 类 它使我能够将查询字符串附加到 url 中 并根据传递的变量对其进行处理 如何才能做到这一点 Eg www example com var1 a var2 b var3 c 现在我想要得到 var1 a var
  • 在 Lyx 中使用 Knitr 时出现 R 函数错误

    使用summary 函数时出现错误 Lyx 中的针织者 它前面的函数可以工作 lt lt gt gt library faraway head teengamb mdl lt lm gamble sex status data teenga
  • 如何在 Jenkins 托管的网格上使用 Chromedriver 设置 Selenium

    我刚刚迈出了使用 Selenium 的第一步 我成功设置了一个测试 Firefox 驱动程序 在我的 Jenkins 上的 Selenium 网格上运行 使用 Jenkins Selenium Grid 插件 我还在运行 Jenkins 的
  • JavaScript sweetAlert 弹出窗口在一秒钟后自行关闭

    我有一个 SweetAlert 弹出窗口 但它会自动关闭 通常它应该保留到用户单击 确定 为止 我已经包含并测试了所有 SweetAlert 文件
  • 通过 RStudio 加载 com.databricks.spark.csv

    我已经安装了Spark 1 4 0 我还安装了它的 R 包 SparkR 并且可以通过 Spark shell 和 RStudio 使用它 但是 有一个我无法解决的差异 启动 SparkR shell 时 bin sparkR master
  • for 循环中的 MATLAB 和元胞数组处理

    我是 MATLAB 新手 想从数据库中获取的元胞数组中提取数据 sensors 1 23 1 0 0 1000 1x29 char 2 23 1 120 0 1000 1x43 char 3 23 1 120 0 1000 1x42 cha
  • FaceBook 应用程序:检索我的应用程序用户的 ID 列表

    我可以使用 fql 或 graph api 检索它吗 每次用户访问您的应用程序时 都可以像这样检索他的 facebook id facebook new Facebook api key secret facebook gt require
  • 关闭时为详细信息标签设置动画

    我正在为我的历史项目开发一个网站 但遇到了一个问题 我尝试为详细信息标签设置动画 但似乎没有任何效果 这是我用于打开动画的代码 keyframes open 0 opacity 0 transform translateY 1vw 100
  • 如何在 Visual Studio 2010 中打开 IntelliSense?

    我正在尝试为 aspx 文件类型启用智能感知 我在文本编辑器选项中检查了自动列表成员 勾选隐藏高级会员与否没有什么区别吗 如何在 vs net 2010 中启用智能感知 应立即为 ASP NET 启用 IntelliSense 包括 asp
  • Python 将图像转换为使用更少的颜色

    我想拍摄一张图像并 以某种方式 将其读取为像素数组 这意味着 2d 数组的每个元素都是表示该像素颜色的十六进制代码或 RGB 三元组 我研究过图像处理 发现了 Pillow 或 SciPy 之类的东西 但我只发现了过于简单的东西 例如添加过
  • Python 正则表达式匹配无法匹配孟加拉语句子中的特定单词

    这里有奇怪的小问题 我有孟加拉语 随机 句子 我尝试在其上运行正则表达式 使用Pythonre库 像这样 令牌 4 re search r b b
  • Square 有沙盒测试帐户吗?

    我正在尝试为 Square Connect API 开发一个包装器 我正在寻找沙盒帐户或将测试数据导入新帐户的方法 以便我可以快速开始开发方面 谢谢你 为了供从 Google 登陆这里的人们将来参考 Square 现在提供此功能 每个帐户都
  • 读取原子修改的值是否需要内存屏障?

    鉴于以下情况 class Foo public void Increment InterlockedIncrement m value OSIncrementAtomic long GetValue return m value priva