问题标签 [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.

0 投票
5 回答
49904 浏览

concurrency - 什么是记忆栅栏?

使用显式内存栅栏是什么意思?

0 投票
5 回答
501 浏览

multithreading - 原子指令和变量更新可见性

在最常见的平台上(最重要的是 x86;我知道有些平台的内存模型非常困难,几乎不能保证对多线程有用,但我不关心罕见的反例),下面的代码安全吗?

线程 1:

线程 2:

假设原子操作的标准、合理实现:

  1. 线程 1 的分配是否保证在被调用someVariable之前完成?atomicSet()
  2. 如果线程 2 以原子方式读取,是否保证someVariable在调用之前看到分配?doMoreStuff()stuffDoneFlag

编辑:

  1. 我正在使用的原子操作的实现LOCK在每个操作中都包含 x86 指令,如果有帮助的话。
  2. 假设stuffDoneFlag以某种方式正确清除。怎么样并不重要。
  3. 这是一个非常简化的例子。我以这种方式创建它,这样您就不必了解问题的整个背景来回答它。我知道它没有效率。
0 投票
4 回答
447 浏览

c# - 写入/读取何时影响主内存?

当我将值写入字段时,关于新值何时保存在主存储器中,我能得到什么保证?例如,我怎么知道处理器没有将新值保存在它的私有缓存中,而是更新了主内存?
另一个例子:

是否有可能在Write()完成执行后,其他一些线程执行Read()但实际上会将“0”视为当前值?(因为也许之前对 m_foo 的写入还没有刷新?)。
什么样的原语(除了锁)可用于确保写入被刷新?


编辑
在我使用的代码示例中,写入和读取放置在不同的方法中。Thread.MemoryBarrier 不只影响存在于同一范围内的指令重新排序吗?

另外,假设它们不会被 JIT 内联,我如何确保写入 m_foo 的值不会存储在寄存器中,而是存储到主存储器中?(或者当 m_foo 被读取时,它不会从 CPU 缓存中获取旧值)。

是否可以在不使用锁或“volatile”关键字的情况下实现这一目标?(另外,假设我没有使用原始类型,而是使用 WORD 大小的结构[因此无法应用 volatile]。)

0 投票
3 回答
1602 浏览

.net - .NET 内存模型、易失性变量和测试和设置:有什么保证?

我知道 .NET 内存模型(在 .NET Framework 上;不是 compact/micro/silverlight/mono/xna/what-have-you)保证对于某些类型(最显着的原始整数和引用)操作保证是原子。

此外,我相信 x86/x64 测试和设置指令 (and Interlocked.CompareExchange) 实际上引用了全局内存位置,因此如果它成功,另一个Interlocked.CompareExchange会看到新值。

最后,我相信该volatile关键字是对编译器的指令,以尽快传播读取和写入,并且不对有关此变量的操作重新排序(对吗?)。

这导致了几个问题:

  1. 我上面的信念是正确的吗?
  2. Interlocked.Read没有 int 的重载,仅适用于 longs(这是 2 个 WORD,因此通常不会以原子方式读取)。我一直认为 .NET 内存模型保证在读取整数/引用时会看到最新的值,但是对于处理器缓存、寄存器等。我开始看到这可能是不可能的。那么有没有办法强制重新获取变量?
  3. volatile 足以解决整数和引用的上述问题吗?
  4. 在 x86/x64 上,我可以假设...

如果有两个全局整数变量 x 和 y,如果我写的话,都初始化为 0:

该 NO 线程将看到 x = 0 和 y = 2(即写入将按顺序发生)。如果它们不稳定,这会改变吗?

0 投票
1 回答
2769 浏览

c++ - 记忆栅栏 - 需要帮助来理解

我正在阅读 Paul E. McKenney 的 Memory Barriers http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.07.23a.pdf 一切都得到了详细的解释,当我看到一切都很清楚时我遇到一句话,让一切变得晦涩难懂,让我觉得我什么都不懂。让我举个例子

假设这两个函数在不同的处理器上运行。现在可能发生的情况是,在存储到 b #2 之后,第二个处理器可以看到存储到 a #1,因为第一个处理器将存储到“a”并继续存储 b 指令。好的,没关系,我们在 #1 和 #2 之间的行中添加了一个写栅栏,但是这段代码仍然可能失败,因为第二个处理器可能会将无效消息排队,所以我们在其中添加了一个内存栅栏(这次是读栅栏) #4 和 #4 之间的线。

这会强制第二个处理器处理所有排队的消息(使 a 无效)并通过在 #4 上向第一个处理器发送读取的 MESI 消息来再次读取它。好的。接下来文章说

因此,许多 CPU 架构提供了较弱的内存屏障指令,它们只执行这两者中的一个或另一个。粗略地说,“读内存屏障”仅标记无效队列,“写内存屏障”仅标记存储缓冲区。而成熟的内存屏障两者兼而有之。

很好,很清楚,但在那之后我看到了

这样做的效果是,读内存屏障命令仅在执行它的 CPU 上加载,因此读内存屏障之前的所有加载似乎都在读内存屏障之后的任何加载之前完成。类似地,写内存屏障只命令存储,再次在执行它的 CPU 上,并且再次在写内存屏障之前的所有存储将看起来在写内存屏障之后的任何存储之前完成。

所以

读内存屏障之前的所有加载似乎都在读内存屏障之后的任何加载之前完成

这混淆了之前解释的所有内容。这是什么意思?函数“bar”中的哪个加载必须在加载“a”#4 之前完成?我知道断言可能会在没有内存屏障的情况下失败,因为处理器可能会读取旧值,因为它仍然没有设法使其缓存线无效,对象“a”所在的位置。

详细解释真的很有帮助,我整天都在努力理解它。

首先十分感谢。

0 投票
4 回答
766 浏览

c++ - 内存排序问题

我正在试验 C++0x 支持,但有一个问题,我想不应该存在。要么我不理解这个主题,要么 gcc 有一个错误。

我有以下代码,最初x并且y是相等的。线程 1 总是x先递增,然后递增y。两者都是原子整数值,因此增量完全没有问题。线程 2 正在检查 是否x小于y,如果是则显示错误消息。

此代码有时会失败,但为什么呢?这里的问题可能是内存重新排序,但默认情况下所有原子操作都是顺序一致的,我没有明确放宽这些操作。我正在 x86 上编译这段代码,据我所知,它不应该有任何排序问题。你能解释一下问题是什么吗?

结果可以在这里查看。

0 投票
1 回答
1624 浏览

c++ - 获取-释放对乱序执行

我正在考虑原子变量是否可以在获取-释放对中加载旧值。假设我们有原子变量 x,我们用释放语义存储该变量,然后用获取语义加载它,理论上可以读取旧值吗?

如果函数线程 1 在线程 2 加载 x 时完成(因此存储了新值),线程 2 是否可以从 x 加载旧值?换句话说,如果在加载之前完成了对 x 的实际存储,那么断言是否可以触发?

据我从互联网上的文章中了解到,这是可能的,但我不明白为什么。store to x 生成的内存栅栏保证清空存储缓冲区,而从 x 加载时获取内存栅栏保证使缓存行无效,因此它必须读取最新值。

添加

这是否意味着获取释放本身没有任何强制排序?只有在发布之前完成的任何事情都会在发布之前发生,而在获取之后完成的所有事情都会在它之后发生,因此获取-发布对强制对其他操作进行排序(为什么??)。我做对了吗?这是否意味着在下面的代码中断言保证不会触发

当然,如果线程 1 已经完成了商店。如果我们用 while (x.load() == 0) 替换 x.load,这将 100% 有效,但我不知道是什么原因导致它起作用。

如果我用下面的代码替换代码怎么办

它有什么改变吗?

谢谢。

0 投票
3 回答
4445 浏览

assembly - 英特尔 64 和 IA-32 | 原子操作,包括获取/释放语义

根据 Intel 64 和 IA-32 架构软件开发人员手册,LOCK 信号前缀“确保处理器在信号被断言时独占使用任何共享内存”。这可以是总线或缓存锁的形式。

但是 - 这就是我问这个问题的原因 - 我不清楚这个前缀是否也提供了任何内存屏障。

我正在多处理器环境中使用 NASM 进行开发,并且需要使用可选的获取和/或释放语义来实现原子操作。

那么,我是否需要使用 MFENCE、SFENCE 和 LFENCE 指令,或者这会是多余的吗?

0 投票
2 回答
3411 浏览

c++ - Visual C++ x86 上的 volatile 变量和原子操作

普通加载在 x86 上具有获取语义,普通存储具有释放语义,但是编译器仍然可以重新排序指令。虽然栅栏和锁定指令(锁定的 xchg、锁定的 cmpxchg)可以防止硬件和编译器重新排序,但仍然需要纯加载和存储来保护编译器屏障。Visual C++ 提供了 _ReadWriterBarrier() 函数,它可以防止编译器重新排序,C++ 出于同样的原因也提供了 volatile 关键字。我写下所有这些信息只是为了确保我做对了。所以上面写的都是真的,有什么理由将它们标记为 volatile 变量,这些变量将在受 _ReadWriteBarrier() 保护的函数中使用?

例如:

使该变量非易失性是否安全?据我了解,因为函数受到保护,内部编译器无法进行重新排序。另一方面,Visual C++ 为 volatile 变量提供了特殊行为(与标准不同),它使 volatile 读写原子加载和存储,但我的目标是 x86,普通加载和存储在 x86 上应该是原子的无论如何,对吧?

提前致谢。

0 投票
1 回答
531 浏览

c++ - C++0X memory_order 不带栅栏、应用程序、支持的芯片

作为我上一个问题的后续,atomic<T>该类使用参数指定大多数操作memory_order。与栅栏相反,此内存顺序仅影响其操作的原子。大概通过使用几个这样的原子,您可以构建一个并发算法,其中其他内存的顺序并不重要。

所以我有两个问题:

  1. 有人可以指出一个算法/情况的示例,该算法/情况将受益于单个原子变量的排序并且不需要围栏?
  2. 哪些现代处理器支持这种行为?也就是说,编译器不会只是将特定顺序转换为正常的栅栏。