问题标签 [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++ - 了解原子变量和操作
我一遍又一遍地阅读了关于 boost 和 std (c++11) 原子类型和操作的信息,但我仍然不确定我是否理解正确(在某些情况下我根本不理解)。所以,我有几个问题。
我用于学习的资源:
- 提升文档: http: //www.boost.org/doc/libs/1_53_0/doc/html/atomic.html
- http://www.developerfusion.com/article/138018/memory-ordering-for-atomic-operations-in-c0x/
考虑以下代码段:
#1:是否等同于下一个?
#2:以下陈述是真的吗?
*1 行保证,当在此行下完成的操作(例如 *2)是可见的(对于使用获取的其他线程),*1 以上的代码也将是可见的(具有新值)。
Next snipped 扩展了上面的那些:
#3:是否等同于下一个?
#4:以下陈述是否正确?
- *3 行确保如果发布顺序下的某些操作(在其他线程中,如 *2)可见,则发布顺序之上的每个操作(例如 *1)也将可见。
- 这意味着 *5 处的断言永远不会失败(默认值为 false)。
- 但这并不能保证即使在物理上(在处理器中)*2 发生在 *3 之前,它也会通过上面的剪辑可见(在不同的线程中运行) - 函数 read_y_then_x() 仍然可以读取旧值。唯一可以确定的是,如果 y 为真,则 x 也为真。
#5:原子整数的递增(加 1 操作)可以是 memory_order_relaxed 并且不会丢失任何数据。唯一的问题是结果可见性的顺序和时间。
根据 boost,以下截图是工作参考计数器:
#6 为什么要减少使用的 memory_order_release?它是如何工作的(在上下文中)?如果我之前写的是真的,是什么让返回的值是最新的,尤其是当我们在阅读之后而不是之前/期间使用获取时?
#7 为什么引用计数器归零后有获取命令?我们刚刚读到计数器为零并且没有使用其他原子变量(指针本身没有被标记/使用)。
linux-kernel - 为什么 MONITOR 和 MWAIT 之间需要内存屏障?
仔细阅读Linux x86 idle loop,我注意到monitor
和之间存在内存障碍mwait
,我无法弄清楚为什么它是必要的。
smp_mb()
是一个宏asm volatile("mfence":::"memory")
。
为什么这里有必要?我理解为什么需要编译器内存屏障,而不是硬件内存屏障。
c++ - 仅编译器的内存屏障(例如 std::atomic_signal_fence)何时有用?
当我阅读有关内存模型、障碍、排序、原子等的内容时,经常会出现编译器栅栏的概念,但通常它也与CPU 栅栏配对,但正如人们所期望
然而,有时我会读到只适用于编译器的栅栏结构。这方面的一个例子是 C++11std::atomic_signal_fence
函数,它在cppreference.com上声明:
std::atomic_signal_fence 等价于 std::atomic_thread_fence,除了不发出内存排序的 CPU 指令。仅编译器对指令的重新排序被抑制为 order instructs。
我有五个与此主题相关的问题:
顾名思义
std::atomic_signal_fence
,异步中断(例如线程被内核抢占以执行信号处理程序)是仅编译器围栏有用的唯一情况吗?它的用处是否适用于所有架构,包括强排序架构,例如
x86
?是否可以提供一个具体的示例来演示仅编译器围栏的有用性?
使用时,使用和订购
std::atomic_signal_fence
有什么区别吗?(我希望它没有任何区别。)acq_rel
seq_cst
第一个问题可能涵盖了这个问题,但我很好奇,无论如何都要特别问一下:是否有必要使用带有
thread_local
访问权限的栅栏?(如果有的话,我希望编译器专用的栅栏成为atomic_signal_fence
首选工具。)
谢谢你。
c++ - 为什么 CPU 寄存器就像垃圾收集器的根?
为什么 CPU 寄存器就像垃圾收集器的根?当突变体暂停时,垃圾收集器可以扫描根,变量内容被刷新到内存(使用内存栅栏),因此寄存器不包含不在内存中的数据......如果我的理解是正确的......
那你为什么需要扫描它们呢?我认为这是冗余...
c - C11 内存栅栏使用
即使对于一个简单的 2 线程通信示例,我也很难用 C11 atomic 和 memory_fence 风格来表达这一点,以获得正确的内存排序:
共享数据:
生产者线程:
消费者线程:
据我了解,上述代码将正确排序存储桶-> 标志存储-> 标志加载-> 从存储桶加载。但是,我认为从存储桶加载和用新数据重新写入存储桶之间仍然存在竞争条件。要在桶读取之后强制执行订单,我想我需要atomic_thread_fence()
在桶读取和以下 atomic_store 之间进行显式操作。不幸的是,似乎没有任何memory_order
论据可以对前面的负载强制执行任何操作,甚至memory_order_seq_cst
.
一个非常肮脏的解决方案可能是在消费者线程中重新分配bucket
一个虚拟值:这与消费者只读概念相矛盾。
在旧的 C99/GCC 世界中,我可以使用__sync_synchronize()
我认为足够强大的传统。
同步这种所谓的反依赖关系的更好的 C11 风格的解决方案是什么?
(当然我知道我应该更好地避免这种低级编码并使用可用的高级构造,但我想了解......)
c++ - 使用 volatile 布尔变量进行忙碌等待
这个问题是在阅读了其他开发人员编写的一些代码后出现的,所以我做了一些研究,发现了 Andrei Alexandrescu 的文章。在他的文章中,他说可以使用 volatile 布尔变量来忙等待(参见第一个示例等待/唤醒)
我真的不明白它是如何工作的。
- volatile 不保证操作是原子的。实际上,对布尔变量的读/写是原子的,但理论并不能保证这一点。从我的角度来看,上面的代码可以使用 C++11 安全地重写,方法是使用 std::atomic::load/store 函数和相应的获取/释放内存排序约束。
- 在所描述的示例中我们没有这个问题,但是如果我们有不止一个写入,我们可能会遇到内存排序问题。Volatile 不是栅栏,它不强制内存排序,它只是阻止编译器优化。
那么为什么这么多人使用 volatile bool 来忙等待,它真的便携吗?
c# - .Net 内存可见性行为
编辑:我终于写了一篇关于这个问题的完整文章:同步、内存可见性和泄漏抽象
我用这段代码展示了volatile 读取的重要性:
正如预期的那样,线程不知道新ok
值并且程序挂起。
但是为了获得这种行为,我必须在while
循环中做一些事情,例如增加一个整数。
如果我删除该++n
语句,线程将读取新值并退出。
我想这与JITter 优化有关,因为就 CIL 而言,什么都没有(至少对于像我这样的外行来说):
而且,相反,我天真地期望在循环中执行某些操作会增加线程触发缓存刷新的几率。
我又错过了什么?
最终编辑:这又是一些JITter黑魔法。
感谢 Hans 确认这是一个“众所周知的”JITter“问题”,并指出在x64中我们得到了“预期的”行为。
感谢 MagnatLU提供了生成的汇编代码并分享了一些调试智慧。
c# - 临界区可以无限期地停止处理器吗?
所以想象我有类似于以下的代码:
由于循环将永远运行,并且由于循环处于关键部分:
这会占用处理器的所有时间,永远不会让步给另一个线程吗?我相信一旦处理器处于临界区,它就不能被中断,直到它离开临界区,所以锁中的操作是原子的。
这种技术可以用来保证你的应用程序总是有一个内核可以运行,并且永远不会等待吗?
c# - 在哪里放置栅栏/内存屏障以保证新的读取/提交写入?
像许多其他人一样,我一直对易失性读/写和栅栏感到困惑。所以现在我试图完全理解这些是做什么的。
因此,volatile 读取应该 (1) 表现出获取语义和 (2) 保证读取的值是新鲜的,即,它不是缓存值。让我们关注(2)。
现在,我已经读到,如果您想执行易失性读取,您应该在读取之后引入一个获取栅栏(或完整栅栏),如下所示:
这究竟如何防止读取操作使用先前缓存的值?根据栅栏的定义(不允许在栅栏上方/下方移动读取/存储),我会在读取之前插入栅栏,防止读取穿过栅栏并及时向后移动(又名,被缓存)。
防止读取及时向前移动(或后续指令及时向后移动)如何保证易失性(新)读取?它有什么帮助?
同样,我认为 volatile 写入应该在写入操作之后引入栅栏,防止处理器及时向前移动写入(也就是延迟写入)。我相信这会使处理器刷新对主存的写入。
但令我惊讶的是,C# 实现在写入之前引入了栅栏!
更新
根据这个例子,显然取自“C# 4 in a Nutshell”,放置在写入之后的栅栏 2 应该强制写入立即刷新到主内存,放置在读取之前的栅栏 3 应该保证新鲜读物:
本书中的想法(以及我个人的信念)似乎与 C#VolatileRead
和VolatileWrite
实现背后的想法相矛盾。
gcc - 为什么需要同步屏障?
我有两个 pthreads 正在读取/写入共享内存位置。在一个线程中,我不断检查内存位置的更新。(Linux, Glibc)
线程 1:
线程 2:
这里的要点是,即使过了一段时间我也看不到更新。如果我按如下方式使用同步屏障,则更新立即可见:
所以我的问题是:
- 为什么过了很久还是看不到更新?
- __sync_synchronize() 究竟做了什么?
编辑 1:我理解为什么更新可能不会立即可见。我的问题特别是为什么即使在很长一段时间后也看不到它。