问题标签 [memory-barriers]

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 投票
1 回答
582 浏览

vb.net - 取消引用字段时是否需要内存屏障(.net x86 或 x64)?

在如下代码中,如果 Proc1 和 Proc2 在不同的处理器上同时执行,ThingVal2 是否有可能获得 5 以外的值(例如零)?

我知道在像 IA64 这样的弱模型中,Proc2 很有可能会看到 ThingRef 发生了变化,但没有看到 Thing2 的字段 X 发生了变化。在 x86 或 x64 上运行的 .Net 应用程序是否存在这种风险?如果 Proc1 创建了一个 SimpleThing 的新实例,将其 X 字段设置为 5,然后将 ThingRef 设置为指向它,这是否足以避免危险,或者是否有可能将新事物分配在缓存行上与 Proc2 线程访问过的其他东西共享?

多线程代码的一个常见范例是构造一个不可变对象并设置一个可变引用来指向它(可能使用 Interlocked.CompareExchange)。在 x86/x64 下读取不可变类型而不考虑线程是否总是安全的,还是会导致问题?如果是后者,在 vb.net 中保证可靠行为的首选方式是什么?

此外,是否有任何方法可以指定代码必须以不会发生此类问题的方式运行(例如,将执行限制在诸如 IA64 之类的单个内核上,否则无法保证正确运行)?

0 投票
3 回答
2676 浏览

assembly - x86 上的顺序一致的原子负载

我对 x86 上的顺序一致的加载操作很感兴趣。

据我从汇编程序列表中看到的,由编译器生成它是作为 x86 上的普通加载实现的,但是据我所知,普通加载保证具有获取语义,而普通存储保证具有释放。

顺序一致的存储实现为锁定的 xchg,而加载为普通加载。这听起来很奇怪,你能详细解释一下吗?

添加

刚刚在互联网上发现,只要使用锁定的 xchg 完成存储,就可以像简单的 mov 一样完成顺序一致的原子加载,但是没有证据,也没有文档链接。

0 投票
1 回答
179 浏览

c# - 全内存屏障和 ExclusiveReceiverGroup

使用以下代码:

我是否需要围绕 totalSum += computeResult.Result 生成一个完整的内存屏障?ExclusiveReceiverGroup的Receiver注册中的handler会被线程池调用,因为dispatcherQueue不使用Dispatcher。我读过线程池为它调用的回调生成了一个内存屏障,但这是否只是保证回调引用本身的新鲜度?

ExclusiveReceiverGroup 不会与任何其他代码同时运行,因此通过computationResult.Result 递增totalSum 不必是原子的。我知道 Interlocked.Add 隐式生成一个完整的栅栏,但我只是想看看我是否可以不使用它而逃脱。

这是一个理论问题。我实际上没有像上面的示例代码那样的任何代码,也没有此类代码的任何用例。所以,我想避免“使用 Interlocked.Add 以防万一”的答案。这更像是一个“让我们学习新东西”的问题。

0 投票
1 回答
1163 浏览

c++ - 编译器和 CPU 重新排序

我有以下这种情况。

我的问题是,CPU 或编译器是否有可能重新排序SetValues()函数行?

0 投票
2 回答
2509 浏览

c++ - C++0x 中的栅栏,一般只保证原子或内存

C++0x 草案有一个栅栏的概念,这似乎与 CPU/芯片级别的栅栏概念非常不同,或者说 linux 内核人员对栅栏的期望。问题是草案是否真的暗示了一个极其受限的模式,或者措辞很差,实际上暗示了真正的围栏。

例如,在29.8 Fences下,它声明如下:

如果存在原子操作 X 和 Y,则释放栅栏 A 与获取栅栏 B 同步,两者都对某个原子对象 M 进行操作,使得 A 在 X 之前排序,X 修改 M,Y 在 B 之前排序,并且 Y 读取值由 X 写入或由假设释放序列中的任何副作用写入的值 X 将在它是一个释放操作时开始。

它使用这些术语atomic operationsatomic object. 草案中定义了这样的原子操作和方法,但是否仅指这些?释放围栏听起来像商店围栏。不能保证在栅栏之前写入所有数据存储栅栏几乎是无用的。类似于加载(获取)围栏和完整围栏。

那么,C++0x 中的栅栏/栅栏是不是适当的栅栏和措辞非常糟糕,或者它们是否像描述的那样受到严格限制/无用?


就 C++ 而言,假设我有这个现有的代码(假设栅栏现在可以作为高级构造使用——而不是说在 GCC 中使用 __sync_synchronize ):

假设 a,b,c 的大小可以在平台上拥有原子副本。上面的意思是c永远不会被赋值9。请注意,我们不在乎线程 B 何时看到a==5,只是在它看到时它也会看到b==9

C++0x 中保证相同关系的代码是什么?


答案:如果您阅读我选择的答案和所有评论,您将了解情况的要点。C++0x 似乎迫使您使用带栅栏的原子,而普通的硬件栅栏没有这个要求。在许多情况下,只要sizeof(atomic<T>) == sizeof(T)和 ,这仍然可以用来替换并发算法atomic<T>.is_lock_free() == true

不幸的是,这is_lock_free不是 constexpr。这将允许它在static_assert. 退化atomic<T>为使用锁通常是一个坏主意:与使用互斥锁设计的算法相比,使用互斥锁的原子算法将存在可怕的争用问题。

0 投票
1 回答
733 浏览

c# - 非阻塞同步(MemoryBarrier)

我修改了在非阻塞同步上给出的程序如下:

文章指出they ensure that if B ran after A, reading _complete would evaluate to true.>> 它们意味着内存屏障。

即使我消除了内存障碍,输出也没有变化。并且不确定条件是否为真。

我是否以错误的方式解释它?

谢谢。

0 投票
1 回答
1702 浏览

multithreading - __faststorefence 的行为是什么?

关于这个问题,我只对 x86 和 x86-64 感兴趣。

对于 MSVC 2005, __faststorefence 的文档说:“保证每个先前的存储在任何后续存储之前都是全局可见的。”

对于 MSVC 2008 和 2010,它更改为:“保证每个先前的内存引用,包括加载和存储内存引用,在任何后续内存引用之前都是全局可见的。”

后者的编写方式,在我看来这意味着这也会阻止 CPU 在旧存储之前对负载进行重新排序。这与第一个定义不同,这意味着内在函数仅用于处理阻塞或非临时存储与旧存储的重新排序(x86(-64)唯一的其他重新排序)。

但是,文档似乎自相矛盾:“在 x64 平台上,此例程生成的指令比sfence指令更快的存储栅栏。在 x64 平台上使用此内在函数而不是 _mm_sfence。”

这意味着它仍然具有类似 sfence 的功能,因此仍然可以使用旧商店重新排序加载。那么它是哪一个?有人可以解决我的困惑吗?

PS:寻找这个函数的GCC版本,我遇到了,long local; __asm__ __volatile__("lock; orl $0, %0;" : : "m"(local));但我认为它来自32位代码;什么是 64 位模拟?

0 投票
3 回答
4675 浏览

java - java中的volatile变量和内存屏障

我有一个由链接节点组成的数据结构。您可以将其视为一个简单的 LinkedList。列表的每个节点都包含一些值和指向另一个节点的下一个字段,如果它是最后一个节点,则为 null。第一个节点作为根工作,它没有任何值,它只指向下一个节点。所有其他节点实际上是不可变的,即一旦它们被创建,它们的值和它们的下一个字段在生命周期内都不会改变,除非正在处理与特定情况相关的结构。

一个(只有一个)线程将新节点添加到列表的前面。它是通过构造一个新对象,设置其字段并将下一个字段设置为根指向的对象,然后将根的下一个字段设置为这个新节点来完成的。

其他节点浏览仅执行读取的结构。它们具有对根节点的引用,然后遍历其他节点,直到找到要查找的内容或到达列表的末尾。

我的问题是:是否足以使下一个字段易变?根据我对 java 内存模型的理解,如果主线程(添加新节点的线程)在添加新节点时会执行 volatile 写入,那么一切都会很好地同步,不会发生不一致。

假设在 x86 架构上读取 volatile 变量不会导致任何性能下降是否正确?由于其他线程会经常浏览读取下一个字段的结构,因此可以在没有任何内存障碍等的情况下自由完成这一点很重要。

我还有一个担忧。将要浏览结构的线程也将持有一些额外的节点。这些节点将完全是线程本地的,即它们将仅由创建它们的线程使用,并且根本不会被共享。对于这些额外的节点,下一个字段没有必要是易失的。此外,设置 volatile next 字段将产生内存屏障,这将导致不希望的性能损失。我想知道有没有办法避免这种情况。理想情况下,如果下一个字段有时作为易失性字段工作,有时作为正常字段工作,那将是完美的;)或者如果我有完全的控制权并且可以在我需要的时候自己发出内存屏障。

编辑:

我还想知道是否有可能以某种方式将所有这些写入同步到不同的 volatile 变量上?例如其他一些完全不相关的静态变量?由于 volatile 写入会刷新所有挂起的写入,难道下一个字段不是 volatile 而是在更新线程完成所有工作后写入不同的 volatile 变量吗?

对我来说它看起来不太安全,因为在关系之前没有发生任何事情,并且之前的写入可能会被重新排序。下一个字段分配可以使用值字段分配重新排序,从而导致迭代线程观察不一致的对象状态。

但也许有可能想出这样一个安全的方案?这个怎么样:

更新线程首先构造一个新对象,初始化其值字段,将其下一个字段设置为根节点指向的节点,对某个静态变量执行易失性写入,将根节点的下一个字段设置为新创建的节点

0 投票
1 回答
779 浏览

c# - 锁助手的线程安全使用(关于内存屏障)

通过锁定助手,我指的是可以通过using语句实现锁定的一次性对象。例如,考虑Jon Skeet 的 MiscUtilSyncLock类的典型用法:

现在,考虑以下用法:

我的问题是这个 - 因为example是在一个线程上创建并在另一个线程ConcurrentMethod上调用的,所以ConcurrentMethod线程不能忘记_padock构造函数中的赋值(由于线程缓存/读写重新排序),因此抛出一个NullReferenceException(on_padLock本身)?

我知道使用Monitor/锁定具有lock内存屏障的好处,但是当使用诸如此类的锁定助手时,我不明白为什么可以保证这样的屏障。在这种情况下,据我了解,必须修改构造函数:

来源:了解低锁技术在多线程应用程序中的影响

编辑Hans Passant 建议创建线程意味着内存屏障。那么怎么样:

现在不一定要创建线程...

0 投票
3 回答
4620 浏览

c# - 内存屏障生成器

阅读Joseph Albahari 的线程教程,以下内容被提及为内存屏障的生成器:

  • C# 的lock语句 ( Monitor.Enter/ Monitor.Exit)
  • Interlocked类的所有方法
  • 使用线程池的异步回调——包括异步委托、APM 回调和任务延续
  • 设置和等待信号结构
  • 任何依赖于信号的东西,例如启动或等待一个任务

此外,Hans Passant 和 Brian Gideon添加了以下内容(假设其中没有一个已经属于前面的类别之一):

  • 启动或唤醒线程
  • 上下文切换
  • Thread.Sleep()

我想知道这个列表是否完整(如果甚至可以实际制作完整的列表)

编辑添加建议:

  • 易失性(读取意味着获取栅栏,写入意味着释放栅栏)