问题标签 [memory-fences]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - C++ 线程与可见性问题——常见的工程实践是什么?
从我的研究中,我知道了饥饿、死锁、公平和其他并发问题的概念。然而,在一定程度上,理论与实践有所不同,真正的工程任务往往涉及比学术等等更多的细节……
作为一名 C++ 开发人员,我关注线程问题已经有一段时间了……
假设您有一个共享变量x
,它引用程序内存的较大部分。该变量在两个线程之间共享,A
并且B
.
x
现在,如果我们考虑对线程A
和线程的读/写操作B
,可能同时,需要同步这些操作,对吧?因此,访问x
需要某种形式的同步,例如可以通过使用互斥锁来实现。
现在让我们考虑另一种情况,x
最初由 thread 写入A
,然后传递给 thread B
(以某种方式)并且该线程仅读取x
. 然后线程产生对被调用B
的响应并将其传递回线程(再次,以某种方式)。我的问题是:我应该使用什么同步原语来使这个场景线程安全。我读过原子,更重要的是,内存栅栏——这些是我应该依赖的工具吗?x
y
A
这不是存在“关键部分”的典型场景。相反,一些数据在线程之间传递,不可能在同一内存位置并发写入。因此,在写入数据之后,首先应该以某种方式“刷新”数据,以便其他线程在读取之前可以看到它处于有效且一致的状态。文献中怎么称呼,是不是“能见度”?
怎么样pthread_once
和它的 Boost/std 对应物,即call_once
. 如果两者都通过一种“消息队列”在线程之间传递并通过“一次”功能访问,这x
是否有帮助。y
AFAIK 它充当一种记忆栅栏,但我找不到任何确认。
CPU 缓存及其一致性如何?从工程的角度来看,我应该知道什么?这些知识对上述场景或 C++ 开发中常见的任何其他场景有帮助吗?
我知道我可能会混合很多主题,但我想更好地了解常见的工程实践是什么,以便我可以重用已知的模式。
这个问题主要与 C++03 中的情况有关,因为这是我的日常工作环境。由于我的项目主要涉及 Linux,所以我可能只使用 pthreads 和 Boost,包括 Boost.Atomic。但我也很感兴趣,如果随着 C++11 的出现,与此类问题有关的任何事情发生了变化。
我知道这个问题是抽象的,不是那么精确,但任何输入都可能有用。
concurrency - 这些获取和释放栅栏的定义不正确吗?
在 Joe Duffy 的并发编程书中,他定义了获取和释放栅栏,如下所示:
• 获得栅栏。确保围栏之后的负载或存储不会在围栏之前移动。之前的指令可能仍然会在围栏之后移动。
• 释放围栏。确保栅栏之前的负载或存储不会在栅栏之后移动。之后的指令可能仍然发生在栅栏之前。
我的问题是:如何允许操作在某事之前发生,但在之后被阻止发生。这很难解释,但对我来说,这两种说法都像是鸡或蛋的问题。
multithreading - 为什么 C++11 的 acquire_release 栅栏不足以进行 Dekker 同步?
Dekker 式同步的失败通常通过重新排序指令来解释。即,如果我们写
然后负载可以与商店重新排序,导致r1==r2==0
.
我期待一个 acquire_release 栅栏来防止这种重新排序:
货物不能移到栅栏上方,仓库不能移到栅栏下方,应防止造成不良后果。
但是,实验表明r1==r2==0
仍然可以发生。对此是否有基于重新排序的解释?我推理的缺陷在哪里?
multithreading - 什么时候需要 x86 LFENCE、SFENCE 和 MFENCE 指令?
好的,我一直在阅读 SO 中关于 x86 CPU 围栏(和)的LFENCE
以下SFENCE
问题MFENCE
:
和:
老实说,我仍然不确定何时需要围栏。我试图从删除完全成熟的锁的角度来理解,并尝试通过栅栏使用更细粒度的锁定,以最大限度地减少延迟延迟。
首先,这里有两个我不明白的具体问题:
有时,在进行存储时,CPU 会写入其存储缓冲区而不是 L1 缓存。但是,我不明白 CPU 会执行此操作的条款?
CPU2 可能希望加载一个已写入 CPU1 存储缓冲区的值。据我了解,问题是 CPU2 无法在 CPU1 的存储缓冲区中看到新值。为什么 MESI 协议不能只将刷新存储缓冲区作为其协议的一部分?
更一般地说,有人可以尝试描述整体情况并帮助解释何时/LFENCE
需要说明吗?MFENCE
SFENCE
NB 围绕这个主题阅读的一个问题是,当我只对 Intel x86-64 架构特别感兴趣时,“一般”为多个 CPU 架构编写的文章数量。
multithreading - 如果通过标记为WC(Write Combined)的PCIe写入远程内存,那么我们是否会自动保持一致性?
正如我们在 x86 架构上所知道的那样,自动提供了获取-释放一致性——即所有操作自动排序,没有任何栅栏,不包括来自不同位置的第一次存储和下一次加载操作。(如第 34 页 Herb Sutter 所说:https ://onedrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&authkey=!AMtj_EflYn2507c )
正如我们所知,当我们通过 FSB写入远程WC 标记的内存时,CPU 使用大小为 64 字节的临时缓冲区 - WCB(写入组合缓冲区)/BIU(总线接口单元)。并且“当 WCB 最终通过 FSB 转储到外部存储器时,数据不一定按照早期程序存储的执行顺序写入内存。” 即我们没有自动获取-释放的一致性——引用自如果我们将内存标记为 WC(Write Combined),那么我们是否有任何自动一致性? 有关详细信息,请参阅第 1080 页上的“WCB FSB 事务”。
但是,如果我们通过 PCI Express写入远程WC 标记的内存会发生什么,当我们使用 MOV 或 SSE 时,我们是否会具有自动获取-释放的一致性?
assembly - 为什么(或不是?)SFENCE + LFENCE 等同于 MFENCE?
正如我们从之前对处理器 x86/x86_64 中的指令 LFENCE 是否有意义的回答中所知道的那样?我们不能用它SFENCE
来代替MFENCE
顺序一致性。
那里的答案表明MFENCE
= SFENCE
+ LFENCE
,即它LFENCE
做了一些我们无法提供顺序一致性的事情。
LFENCE
无法重新排序:
-- 到 -->
例如,由机制 - Store BufferMOV [addr], reg
LFENCE
提供的--> 重新排序,它重新排序 Store - Loads 以提高性能,因为不会阻止它。并禁用此机制。LFENCE
MOV [addr], reg
LFENCE
SFENCE
什么机制禁用了LFENCE
不可能的重新排序(x86 没有机制 - Invalidate-Queue)?
SFENCE
MOV reg, [addr]
对-->的重新排序是否MOV reg, [addr]
SFENCE
仅在理论上或实际上可能?如果可能的话,实际上是什么机制,它是如何工作的?
c++ - std::atomic::load 的内存排序行为
我是否错误地假设 atomic::load 也应该充当内存屏障以确保所有以前的非原子写入都将被其他线程看到?
为了显示:
线程1:
线程2:
我预计在执行两个“触发”之后都是真的。请不要建议将 arm1 设为原子,重点是探索 atomic::load 的行为。
虽然我不得不承认我并不完全理解内存顺序的不同宽松语义的正式定义,但我认为顺序一致的顺序非常简单,因为它保证“存在一个单一的总顺序,其中所有线程都观察所有修改以相同的顺序。” 对我来说,这意味着具有 std::memory_order_seq_cst 的默认内存顺序的 std::atomic::load 也将充当内存围栏。以下“顺序一致的排序”下的声明进一步证实了这一点:
完全顺序排序需要所有多核系统上的完整内存栅栏 CPU 指令。
然而,我下面的简单示例演示了 MSVC 2013、gcc 4.9 (x86) 和 clang 3.5.1 (x86) 的情况并非如此,其中原子加载只是转换为加载指令。
使用 gcc 这看起来像:
我将省略本质上相同的 msvc 和 clang。现在在 ARM 的 gcc 上,我们得到了我所期望的:
这不是一个学术问题,它在我们的代码中导致了一个微妙的竞争条件,这让我对 std::atomic 的行为的理解产生了疑问。
c++ - 在运行时检测负载是否是原子的?
我的应用程序需要几个原子加载和存储。不幸的是,这些操作必须发生在内存映射文件中的特定地址,所以我不能使用 c++11 的 std::atomic (因为 std::atomic 通过控制变量的大小和对齐/放置来工作。因为我控制内存映射文件格式,我们只在单个 CPU 架构上运行
有没有办法测试(在运行时)对特定地址的特定大小的读取或写入是否是原子的?我的主要平台是 x86-64,但我也对 ARM 的解决方案感兴趣。
java - 将 C++ 内存排序映射到 Java
我正在将一个小的 C++ 片段翻译成 java,我对内存排序/栅栏不是 100% 有信心。这个对吗:
C++:
我认为它应该如何转换为 Java:
这是正确的路线吗?(是的,使用 unsafe 与仅使用 AtomicLong 是有原因的)
objective-c - dispatch_async() 和类似的是否提供内存栅栏?
当我通过或类似方式在任何队列上运行块时dispatch_async
,GCD 是否在块调用周围提供线程围栏?我会假设它确实如此,但据我所知,文档没有给出任何暗示。