如何理解读内存屏障和易失性

2023-11-30

有些语言提供了volatile修饰符被描述为在读取支持变量的内存之前执行“读内存屏障”。

读内存屏障通常被描述为一种确保 CPU 在执行屏障之后请求的读取之前已执行屏障之前请求的读取的方法。然而,使用这个定义,似乎仍然可以读取过时的值。换句话说,以某种顺序执行读取似乎并不意味着必须咨询主存储器或其他CPU以确保后续读取的值实际上反映了读屏障时系统中的最新值或随后在读屏障之后写入的值。读障碍。

那么,易失性是否真的保证读取最新的值,或者只是(喘气!)读取的值至少与屏障之前的读取一样最新?或者还有其他的解释?这个答案有什么实际意义?


有读屏障和写屏障;获得障碍和释放障碍。还有更多(io 与内存等)。

不存在控制“最新”值或值的“新鲜度”的障碍。它们的作用是控制内存访问的相对顺序。

写屏障控制写入的顺序。由于写入内存的速度很慢(与 CPU 的速度相比),因此通常有一个写入请求队列,写入在“真正发生”之前就被发布到其中。尽管它们按顺序排队,但在队列内部,写入可能会重新排序。 (所以也许“队列”不是最好的名字......)除非您使用写屏障来防止重新排序。

读屏障控制读取的顺序。由于推测执行(CPU 提前查看并提前从内存加载)并且由于写入缓冲区的存在(CPU 将从写入缓冲区而不是内存中读取值(如果存在),即 CPU 认为它刚刚写入了 X = 5,那为什么要读回来呢,就看它还在等待become5)读取可能会发生乱序。

无论编译器尝试对生成代码的顺序执行什么操作,情况都是如此。即 C++ 中的“易失性”在这里不起作用,因为它只告诉编译器输出代码以重新读取“内存”中的值,它不会告诉 CPU 如何/从哪里读取它(即“内存”) CPU 级别上有很多事情)。

因此,读/写屏障会设置块来防止读/写队列中的重新排序(读取通常不是一个队列,但重新排序效果是相同的)。

有哪些类型的块? - 获取和/或释放块。

获取 - 例如 read-acquire(x) 会将 x 的读取添加到读取队列中并刷新队列(并不是真正刷新队列,而是添加一个标记,说明在读取之前不要重新排序任何内容,就像刷新队列一样)。因此稍后(按代码顺序)读取可以重新排序,但不能在读取 x 之前重新排序。

释放 - 例如 write-release(x, 5) 将首先刷新(或标记)队列,然后将写入请求添加到写入队列。因此,较早的写入不会在 x = 5 之后重新排序,但请注意,稍后的写入可以在 x = 5 之前重新排序。

请注意,我将读取与获取配对,将写入与释放配对,因为这是典型的,但也可能有不同的组合。

获取和释放被视为“半屏障”或“半栅栏”,因为它们仅阻止重新排序以一种方式进行。

完整的屏障(或完整的栅栏)同时应用获取和释放 - 即没有重新排序。

通常对于无锁编程,或 C# 或 java“易失性”,您想要/需要的是 读-获取和写-释放。

ie

void threadA()
{
   foo->x = 10;
   foo->y = 11;
   foo->z = 12;
   write_release(foo->ready, true);
   bar = 13;
}
void threadB()
{
   w = some_global;
   ready = read_acquire(foo->ready);
   if (ready)
   {
      q = w * foo->x * foo->y * foo->z;
   }
   else
       calculate_pi();
}

因此,首先,这是一种糟糕的线程编程方式。有锁会更安全。但只是为了说明障碍......

threadA() 写完 foo 后,它需要最后写入 foo->ready,真的是最后,否则其他线程可能会提前看到 foo->ready 并得到错误的 x/y/z 值。所以我们使用一个write_release在 foo->ready 上,如上所述,它有效地“刷新”写入队列(确保 x、y、z 已提交),然后将 ready=true 请求添加到队列中。然后添加 bar=13 请求。请注意,由于我们刚刚使用了释放屏障(不是完整的),所以 bar=13 可能会在准备好之前被写入。但我们不在乎!即我们假设 bar 没有更改共享数据。

现在 threadB() 需要知道,当我们说“准备好”时,我们真正的意思是准备好。所以我们做了一个read_acquire(foo->ready)。该读取被添加到读取队列中,然后刷新队列。注意w = some_global也可能仍在队列中。所以 foo->ready 可以被读取before some_global。但同样,我们不在乎,因为它不是我们如此关注的重要数据的一部分。 我们关心的是 foo->x/y/z。所以它们在acquireflush/marker之后被添加到读队列中,保证它们只有在读取foo->ready之后才被读取。

另请注意,这通常与用于锁定和解锁互斥体/关键部分/等的屏障完全相同。 (即在lock()上获取,在unlock()上释放)。

So,

  • 我很确定这(即获取/释放)正是 MS 文档所说的 C# 中“易失性”变量的读/写发生的情况(也可以选择 MS C++,但这是非标准的)。看http://msdn.microsoft.com/en-us/library/aa645755(VS.71).aspx包括“易失性读取具有“获取语义”;也就是说,它保证发生在其之后发生的任何对内存的引用之前......”

  • I thinkjava也是一样,虽然我不太熟悉。我怀疑它是完全相同的,因为您通常不需要比读取获取/写入释放更多的保证。

  • 在你的问题中,当你认为这实际上都是关于相对顺序时,你是在正确的轨道上 - 你只是向后排序(即“读取的值至少与屏障之前的读取一样最新? “ - 不,屏障之前的读取并不重要,其在屏障之后的读取保证在屏障之后,对于写入反之亦然)。

  • 请注意,如上所述,读取和写入都会发生重新排序,因此仅在一个线程上使用屏障而不在另一个线程上使用屏障将不起作用。即,如果没有读取获取,写入释放是不够的。即,即使您以正确的顺序写入它,如果您没有将读取屏障与写入屏障一起使用,也可能会以错误的顺序读取它。

  • 最后,请注意,无锁编程和 CPU 内存架构实际上可能比这复杂得多,但坚持获取/释放会让你走得更远。

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

如何理解读内存屏障和易失性 的相关文章

  • 无法设置指定的 COM 单元状态

    看来我真的不擅长多线程应用程序 我正在尝试打开一个FolderBrowserDialog 但我收到一个异常告诉我 Current thread must be set to single thread apartment STA mode
  • 使用 pythonw.exe 时 Python subprocess.call() 失败

    我有一些 Python 代码 当我使用 python exe 运行时可以正常工作 但如果我使用 pythonw exe 则失败 def runStuff commandLine outputFileName somefile txt out
  • 什么样的应用程序需要多线程?

    什么是一些具体的例子需要或不需要多线程的应用程序 但这样会更好吗 如果答案以每个帖子一个申请的形式最好 这样最适用的就会浮到顶部 没有硬性且快速的答案 但大多数时候 您不会看到工作流程 计算是连续的系统有任何优势 然而 如果问题可以分解为可
  • 确保适配器的内容不会从后台线程修改,而仅从 UI 线程修改

    我有一个带有 EditText 名为 filtro 和 ListView 名为 list view 的活动 当用户插入文本时 适配器应该刷新 如果我多次更改文本 则会发生崩溃 然后显示消息 确保适配器的内容不是从后台线程修改 而是仅从 UI
  • 在 WPF 中处理第二个 UI 线程

    我有一个长时间运行的进程在我的 UI 线程上运行 我无法离开 UI 线程 相反 我尝试创建第二个具有等待动画的 UI 线程 这是我用来创建第二个 UI 线程的代码 Private busyThread As Thread Private w
  • 在 Oracle 过程中实现多线程

    我正在研究 Oracle 10gR2 这是我的问题 我有一个程序 我们称之为 proc parent 在包内 应该调用另一个过程 让我们调用它 用户创建 我得打电话 用户创建 在一个循环中 它从表中读取一些列 并且这些列值作为参数传递给 用
  • 信号量如何工作?

    信号量可以小于0吗 我的意思是 假设我有一个 N 3 的信号量 并且我调用 down 4 次 那么 N 将保持为 0 但一个进程将被阻塞 反之亦然 如果一开始我调用 N 可以大于 3 吗 因为在我看来 如果 N 可以高于 3 如果一开始我调
  • 如何将本机 C 类型与 PerformSelectorOnMainThread: 一起使用?

    我想打电话 void setDoubleValue double value using performSelectorOnMainThread 我认为可行的是 NSNumber progress NSNumber numberWithDo
  • 何时在多线程中使用 易失性?

    如果有两个线程访问全局变量 那么许多教程都说使该变量成为易失性的 以防止编译器将变量缓存在寄存器中 从而无法正确更新 然而 两个线程都访问共享变量需要通过互斥体进行保护 不是吗 但在这种情况下 在线程锁定和释放互斥体之间 代码位于关键部分
  • 在 .NET 4.0 中将任务与 Parallel.Foreach 一起使用

    我开始尝试向 Windows 窗体添加一个进度条 以更新 Parallel Foreach 循环中运行的代码的进度 为此 UI 线程必须可用于更新进度条 我使用 Task 来运行 Parallel Foreach 循环 以允许 UI 线程更
  • 唤醒单个线程而不是 pthread 中的忙等待

    我不确定标题是否反映了我在这里提出的问题 但这是我在没有很长的标题的情况下能做的最好的事情 我正在尝试实施一个worker thread模型中pthreads 我想从中产生一组线程main函数以及此后的main线程将工作委托给工作人员 并等
  • Awaiter (GetAwaiter) 和ContinueWith 有什么区别

    在 net 4 0中 我经常使用Task ContinueWith 但是之后我发现了 task GetAwaiter https stackoverflow com a 14171923 284758这似乎有同样的目的 有什么不同 如果您的
  • Python:threading.timer不尊重间隔

    这是后续另一个问题 https stackoverflow com questions 32286049 python accept input while waiting 我现在有了一个解决方案 但由于不相关的原因 实现似乎没有正常运行
  • 发布/订阅架构

    我尝试编写一个发布 订阅系统 客户端和服务器端 其中客户端接收定期更新 如心跳 消息控制 并可以向服务器发出命令 订阅某些源 这样做的好方法是什么 我已经有一个实现线程池的服务器来管理传入的客户端连接 我想知道如何处理连接双方都可以在 Ne
  • C++并行std::sort用于浮点值

    我有一个包含数百万个浮点值的大文件 我可以使用轻松对它们进行排序std sort通过将文件读入vector现在 例如 std vector
  • C# 中的线程和 GUI 元素

    我正在尝试制作一个基本的 IRC 客户端 但我的问题是让文本显示在 RTF 框中而不出现滞后 我决定使用线程 并且我想更新线程中的 RTF 框 但我不能 因为它给了我关于 RTF 框元素不是静态的错误 有什么见解吗 如果你们想要的话我会粘贴
  • Python Tkinter,停止线程函数

    我目前正在为 3D 打印机开发 GUI 并且遇到如何停止线程函数的问题 我希望能够单击 GUI 中具有另一个功能的按钮 该按钮将阻止线程函数通过串行端口发送 G 代码字符串 目前 该函数已合并线程 以允许在打印期间触发其他函数 我非常感谢有
  • 将 Python 控制台集成到 GUI C++ 应用程序中

    I m going to add a python console widget into a C GUI below some other controls 许多类将暴露给 python 代码 包括一些对 GUI 的访问 也许我会考虑 P
  • SLURM 节点、任务、核心和 CPU

    有人能够澄清这些东西到底是什么吗 据我所知 节点是集群内的计算点 本质上是一台计算机 任务是可以在单个节点或多个节点上执行的进程 核心基本上是指您希望在单个节点上分配多少 CPU 来执行分配给该 CPU 的任务 它是否正确 我混淆了什么吗
  • iPhone 相当于 Application.DoEvents();

    iPHone 我们使用 MonoTouch 但 Obj C 答案还可以 我的单例域对象需要一段时间才能获取所有数据 因此它在线程中内部运行部分获取数据 我需要通知 UI 域已完成 目前我正在这样做 有没有更好的办法 在 WinForms 中

随机推荐

  • 在 LINQ 中选择计数

    我有一个包含列的 SQL Server 表ResolvedDate and ResolvedBy 现在我想选择这两列并计算它们的结果 我想我会这样实现 dataContext Activities Where a gt a IsResolv
  • 比较 Haskell 与 C# 的类型系统,寻找类似物

    我对 Haskell 编程还很陌生 我正在尝试处理它的类 数据 实例和新类型 这是我的理解 data NewData Constr1 Int Int Constr2 String Float 与 Java 或 C 大致相同 class Ne
  • AngularJS 与 MVC 6

    这是一MVC 6 WebApi应用 我正在尝试在后端使用 WebApi 在前端使用 AngularJS 我有两个静态文件 index html 和 login html 最终将会有更多的静态文件 我的 Angular 应用程序包含在inde
  • Quickfix 无法读取重复组

    我在 Windows 中使用 Quickfix 和 python 绑定 我过去曾能够提出市场数据请求 我最近更换了不同的 API 提供商 Cunningham 又名 CTS 并且遇到了很多问题 然而 至少其中之一似乎是 Quickfix 内
  • 在 APL 中创建单位矩阵最惯用的方法

    根据罗塞塔代码 在 APL 中有两种创建单位矩阵的惯用方法 1 ID 2 ID 1 0 2 如何运作 为什么这比 1 更好 后者使用 APL 中惯用的方法外积 1 0创建一个向量 其中包含1其次是 零 所以 这个向量的长度是 1 涵盖 by
  • 如何克隆微软图表控件?

    克隆 Microsoft Chart Control 对象的推荐方法是什么 因为是第三方库 所以无法使用解决方案这里提到因为我无法将对象标记为可序列化 最好 我不想引入任何第三方控件来克隆图表 除非如果没有第三方控件绝对不可能这样做 您不需
  • 将 Fetch API 与 Promise.all 一起使用

    我的目标是从两个 URL 获取数据 并仅在两个 URL 均成功返回时才执行操作 另一方面 如果其中任何一个失败 我想返回错误 我已经尝试了我的代码并设法获得了预期的效果 我的问题是 是否有更有效 更简洁的方法来实现相同的功能 辅助函数 le
  • Fparsec 递归语法抛出 StackOverflowException

    我有这个代码 type Exprs Val of float Mult of Exprs Exprs Plus of Exprs Exprs let pexpr exprRef createParserForwardedToRef
  • 使用 np.linspace() 绘制离散分布

    我正在尝试使用 matplotlib 绘制一个简单的离散分布 如果 1 如果0 如果 1 我该如何开始x np linspace 1 2 到目前为止我尝试过的是 def mapDiscProb x if np any x lt 0 retu
  • 如何使用 iconv 将存储为 LATIN1 ( sql ) 的西里尔字母转换为真正的 UTF8 西里尔字母?

    我有一个 SQL 转储文件 其中包含错误存储的西里尔俄语 WINDOWS 1251 文本 例如 应该正确显示为 过去我已经成功转换了sql文件 但是内存在我所做的事情和顺序上失败了 从逻辑上讲 这是有道理的 因为它存储在 LATIN1 中
  • svmtrain 和 fitcsvm 之间的区别

    我有一组由 35 个特征列表组成的数据 我注意到当我将数据提供给svmtrain我收到消息 no convergence achieved within maximum number of iterations 比 当我增加迭代次数时 Ma
  • 以编程方式从现有 WooCommerce 订单更改运输方式

    我的客户通过我的 woocommerce 网站购买订阅 他们每个月都会收到产品 但有时他们想改变运输方式 我没有找到通过 php 执行此操作的文档 我可以改变其中的值post meta woocommerce order items and
  • 如何检索网络适配器绑定协议的列表? (在 PowerShell 或任何语言中)

    因此 在 Windows UI 中 如果右键单击网络适配器 您会看到一个属性页 其中有一个标记为 此连接使用以下项目 的部分 在此属性页中是一个项目列表 旁边有一个复选框 例如 Microsoft 网络客户端 QoS 数据包调度程序 IP
  • 从 Rails 导出 CSV 数据

    我正在努力从 Rails 导出 CSV 数据 我正在按照这里的教程进行操作 http railscasts com episodes 362 exporting csv and excel view asciicast 在我的控制器中我有
  • Gradle构建错误将字节码转换为dex时出错:原因:com.android.dex.DexException:多个dex文件定义Landroid/arch/lifecycle/liveData$1

    从过去 3 天开始 我收到此错误 并已尝试了所有可能的方法 谁能帮我纠正一下 我正在使用 Android 3 0 我的 sdk 和 Google Play 服务已更新 并且我已添加multiDexEnabled true我的代码 并且还尝试
  • 如何在 swift 中使用模态视图?

    When adding an account to Mail in preferences you get a modal view like this 我的问题是 如何以编程方式复制这个 换句话说 如何在呈现视图上显示模态 UIView
  • Mybatis resulttypehashmap 的 null 值

  • 查找两个单词是否是彼此的字谜

    我正在寻找一种方法来查找两个字符串是否是彼此的字谜 Ex string1 abcde string2 abced Ans true Ex string1 abcde string2 abcfed Ans false 我想出的解决方案是对两个
  • 如果付款失败,PayPal Standard 是否会自动重试向客户收费?

    我必须将 PayPal 标准付款集成到我的 Saas 中才能按月订阅 我在 PayPal 文档中找到了一些有关使用 API 的提示 但这不是我的情况 如果几个月后由于客户没有钱而付款失败 它是否会在接下来的几天内自动重试向客户收费 还是我应
  • 如何理解读内存屏障和易失性

    有些语言提供了volatile修饰符被描述为在读取支持变量的内存之前执行 读内存屏障 读内存屏障通常被描述为一种确保 CPU 在执行屏障之后请求的读取之前已执行屏障之前请求的读取的方法 然而 使用这个定义 似乎仍然可以读取过时的值 换句话说