问题标签 [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++ - 原子vs bool 受互斥锁保护
假设我们有一个内存区域,某个线程正在向其中写入数据。然后它将注意力转移到其他地方并允许任意其他线程读取数据。但是,在某个时间点,它想重用该内存区域并将再次写入。
写线程提供了一个布尔标志(valid
),它表明内存仍然可以读取(即他还没有重用它)。在某个时候,他会将此标志设置为 false 并且永远不会再次将其设置为 true(它只会翻转一次,仅此而已)。
在顺序一致性的情况下,分别为编写者和阅读者使用这两个代码片段应该是正确的:
和
我们显然需要做一些事情来确保顺序一致性,即插入必要的获取和释放内存屏障。在将任何数据写入段之前,我们希望在编写器线程中将该标志设置为 false 。我们希望在检查之前从阅读器线程中的内存中读取数据valid
。后者是因为我们知道valid 是单调的,即如果它读后仍然有效,那么它在读时是有效的。
在内存访问和访问之间插入一个完整的栅栏valid
就可以了。然而,我想知道,如果制造valid
一个原子就足够了吗?
然后
和
似乎在这种情况下,使用原子存储和读取的隐含释放和获取操作对我不利。编写器中的 RELEASE不会阻止将内存访问向上移动(只是上面的代码可能不会向下移动)。同样,阅读器中的 ACQUIRE不会阻止将内存访问向下移动(只是下面的代码可能不会向上移动)。
如果这是真的,为了使这个场景工作,我还需要在编写线程中进行 ACQUIRE(即加载),并在读取线程中进行 RELEASE(即存储)。或者,我可以只使用一个普通的布尔标志,并使用共享互斥锁保护线程中的写入和读取访问(仅限于它!)。通过这样做,我将有效地在两个线程中同时拥有 ACQUIRE 和 RELEASE,从而将valid
访问与内存访问分开。
因此,这将是与受a 保护atomic<bool>
的常规之间非常严重的区别,这是正确的吗?bool
mutex
编辑:实际上,加载和存储原子所暗示的内容似乎有所不同。std::atomic
C++11的memory_order_seq_cst
用于 (!),而不是memory_order_acquire
分别memory_order_release
用于加载和存储。
相比之下,tbb::atomic
使用memory_semantics::acquire
andmemory_semantics::release
而不是memory_semantics::full_fence
.
因此,如果我的理解是正确的,那么对于标准 C++11 原子,代码将是正确的,但对于 tbb 原子,则需要将显式memory_semantics::full_fence
模板参数添加到加载和存储中。
c++ - 在 atomic-acquire-load 之后可以重新排序 non-atomic-load 吗?
众所周知,自 C++11 以来,有 6 个内存顺序,并且在文档中编写了以下内容std::memory_order_acquire
:
memory_order_acquire
具有此内存顺序的加载操作在受影响的内存位置上执行获取操作:在此加载之前,当前线程中的内存访问不能重新排序。这确保了释放相同原子变量的其他线程中的所有写入在当前线程中都是可见的。
1.非原子加载可以在atomic-acquire-load之后重新排序:
即它不保证非原子加载不能在获取原子加载之后重新排序。
加载int local1 = L;
后可以重新排序X.load(std::memory_order_acquire);
吗?
2. 我们可以认为,在 atomic-acquire-load 之后,non-atomic-load 不能重新排序:
一些文章包含一张图片,展示了获取-释放语义的本质。这很容易理解,但可能会引起混乱。
例如,我们可能认为std::memory_order_acquire
不能重新排序任何一系列的 Load-Load 操作,即使是非原子加载也不能在 atomic-acquire-load 之后重新排序。
3.非原子加载可以在atomic-acquire-load之后重新排序:
澄清的好处是:获取语义可防止读取-获取的内存重新排序,并以程序顺序跟随它的任何读取或写入操作。http://preshing.com/20120913/acquire-and-release-semantics/
但也知道:在强排序系统(x86、SPARC TSO、IBM 大型机)上,对于大多数操作来说,发布-获取排序是自动的。
第 34 页的 Herb Sutter 显示:https ://onedrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&authkey=!AMtj_EflYn2507c
4. 即再次,我们可以认为在 atomic-acquire-load 之后 non-atomic-load 不能重新排序:
即对于 x86:
- 对于大多数操作,release-acquire 排序是自动的
- 读取不会与任何读取一起重新排序。(任何 - 即无论是否年龄较大)
那么可以在 C++11 中的 atomic-acquire-load 之后重新排序 non-atomic-load 吗?
c++ - 特定于 MS 的 volatile 是否会阻止硬件指令重新排序
从文档中:
微软特定
当使用 /volatile:ms 编译器选项时(默认情况下,当针对 ARM 以外的体系结构时)编译器生成额外的代码来维护对 volatile 对象的引用之间的顺序,以及维护对其他全局对象的引用的顺序。特别是:
- 对 volatile 对象的写入(也称为 volatile 写入)具有 Release 语义;也就是说,
在指令
序列中写入易失性对象之前发生的对全局或静态对象的引用将发生在已编译
二进制文件中的易失性写入之前。- 对 volatile 对象的读取(也称为 volatile 读取)具有 Acquire 语义;也就是说,
在指令序列中读取易失性存储器之后发生的对全局或静态对象的引用
将发生在编译二进制文件中的易失性读取之后。这使得 volatile 对象可以用于多线程应用程序中的内存锁定和释放。
它肯定会保证volatile
防止编译器执行编译时指令重新排序(因为它明确指出指令序列在编译后的二进制文件中是相同的)。
但我们都知道,还有硬件重排序之类的东西(比如 CPU 能够根据自己的意愿重新排序指令)。是否volatile
也能阻止它?我知道同步原语(例如互斥锁)可以,但是特定于 MS 的volatile
呢?
parallel-processing - 这里需要什么内存栅栏?
作为内存栅栏的必要性以确保并行程序执行的正确性的经典示例,经常给出以下示例:
P1:
P2:
一方面,为了获得最佳性能,处理器尝试(合法地)重新排序指令,以使重新排序的指令序列在考虑顺序执行时具有与原始指令完全相同的效果。另一方面,为了保证顺序执行指令序列的正确性,必须尊重控制依赖。从这个角度来看,P2执行的while语句不就是一个控制依赖吗?如果是这样,那么唯一需要的内存栅栏是 P1 的第 1 行和第 2 行之间的 Store|Store 栅栏,对吗?
opencl - OpenCL 中的障碍
每次我运行这个内核时,我都会得到一些不同的值。我想在第二次循环计算后总结 res 数组(类似于 np.sum(res, axis = 0))。当我在内核外部执行 np.sum 时,我得到相同的值,但在内核内部这是不可能的。可能屏障无法正确隔离内存。任何帮助将非常感激。
c# - System.Threading.Timer 使用的变量是否需要缓存?
我正在尝试对神秘停止的进程进行故障排除,并且我正在尝试确定我们是否会遇到变量被缓存并且不再读取的问题。
在我的场景中,我有一个发生在计时器上的操作(我将调用它Poll
)和一对外部函数,它们被调用以指示外部操作的开始和停止。由于细节不值得深入,这个假设的类不希望在外部操作发生时执行轮询操作。此外,外部操作可能同时发生多次,因此我使用计数器来指示正在进行的操作数。
当前的逻辑看起来或多或少像这个简化的例子:
假设:
- 两者
StartExternalOperation
和EndExternalOperation
都被调用了相同的次数——它们在 a 内被执行,try...finally
并且没有任何路径可以让孤儿Start
坐在外面 - 在不确定数量的执行之后的某个时刻,
DoSomething()
永远不会再次被调用。在我的真实场景中,我不知道确切原因,但我已经消除了除此之外我能找到的所有其他原因。
是否有可能是因为我没有在读取操作中使用任何类型的栅栏(通过Interlocked
或volatile
变量),值被缓存并且实际变量没有再次被读取?
由于这是一个我们无法可靠重现的问题,我需要知道我怀疑的原因是否合理。我可以更改代码以使用更正式的东西,例如ReaderWriterLock
(or Slim
),并且我知道我会有适当的逻辑,我只需要知道我所描述的是否可以合法地成为原因。
c++11 - 无锁同步、栅栏和内存顺序(具有获取语义的存储操作)
我正在将一个在准系统上运行的项目迁移到 linux,并且需要消除一些{disable,enable}_scheduler
调用。:)
所以我需要一个单写多读场景中的无锁同步解决方案,写线程不能被阻塞。我想出了以下解决方案,它不适合通常的获取-发布顺序:
我希望意图很明确:我将(非原子)有效负载的修改包装在 之间beginWrite/endWrite
,并仅在传递给sync()
.
如您所见,这里我有一个原子存储,在beginWrite()
存储操作之后没有写入可以在存储之前重新排序。我没有找到合适的例子,而且我完全没有这方面的经验,所以我想确认一下它是可以的(通过测试验证也不容易)。
这段代码是无竞争的并且可以按我的预期工作吗?
如果我在每个原子操作中使用 std::memory_order_seq_cst ,我可以省略围栏吗?(即使是,我猜性能会更差)
我可以在 endWrite() 中放下围栏吗?
我可以在围栏中使用 memory_order_acq_rel 吗?我真的不明白其中的区别——我不清楚单一的总订单概念。
是否有任何简化/优化的机会?
+1。我很乐意接受任何更好的想法作为这个类的名称:)
c11 - C11 中的非原子栅栏
有没有办法使用栅栏来推理 C11 中非原子操作的行为?具体来说,我希望在某些字段需要为int
s 以与旧接口兼容的情况下使代码安全,例如,可能将数据结构读取和写入文件或将它们作为系统调用参数传递。由于没有要求 aatomic_int
甚至与 a 大小相同int
,因此我不能使用 a atomic_int
。
这是一个最小的工作示例,不幸的是根据第 5.1.2.4 节第 25 节产生未定义的行为,因为数据竞争ready
:
我的具体问题是是否可以修复上述代码以保证打印1
而不更改ready
为_Atomic
. (我可以做ready
一个volatile
,但在规范中看不到任何有帮助的建议。)
一个相关的问题是无论如何编写上述代码是否安全,因为我的代码将在其上运行的任何机器都具有缓存一致性?我知道当 C11 程序包含所谓的良性竞争时,很多 事情都会出错,所以我真的在寻找合理的编译器和架构可以对上述代码做什么的细节,而不是关于数据竞争和未定义的一般警告行为。
c++ - thread::join 上是否存在具有同步关系的隐式内存屏障?
我有一个代码在工作,它启动多个执行某些操作的线程,如果它们中的任何一个失败,它们将共享变量设置为false。
然后主线程加入所有工作线程。对此的模拟大致如下(我注释掉了我不知道是否需要的可能修复):
即使其中一名工作人员将其设置为false,主线程是否有可能将成功变量读取为true?
我发现thread::join()是一个完整的内存屏障(source),但这是否意味着与以下从主线程读取成功变量的同步关系,以便我们保证获得最新值?
在这种情况下,我发布的修复(在注释代码中)是否必要(或者如果这个错误可能是另一个修复)?
是否有可能对成功变量的读取进行优化(因为它不是易失性的)并且我们将获得旧值,而不管thread::join上是否存在隐式内存屏障?
该代码应该适用于多种架构(不记得所有架构,我面前没有makefile)但至少有x86,amd64,itanium,arm7。
感谢您对此的任何帮助。
编辑:我已经修改了示例,因为在实际情况下,一个线程可以尝试写入成功变量。
assembly - 内存屏障是 CPU 执行的指令,还是只是一个标记?
我试图准确地理解什么是内存屏障。根据我目前所知道的,内存屏障(例如:)mfence
用于防止指令从内存屏障之前到之后以及从之后到之前的重新排序。
这是使用中的内存屏障的示例:
现在我的问题是:mfence
指令只是一个标记,告诉 CPU 以什么顺序执行指令?或者它是 CPU 实际执行的指令,就像它执行其他指令一样(例如:)mov
。