什么是内存栅栏?

147

什么是使用显式内存屏障?

5个回答

144

为获得更好的性能,现代CPU通常会乱序执行指令以充分利用可用的硅(包括内存读/写)。由于硬件执行指令的完整性,您在单个执行线程中永远不会注意到这一点。但是,在多个线程或具有易失性内存的环境下(例如内存映射I/O),这可能导致不可预测的行为。

内存屏障是一类指令,意味着内存读/写按照您期望的顺序发生。例如,“全屏障”表示屏障之前的所有读/写在屏障之后提交。

请注意,内存屏障是硬件概念。在更高级别的语言中,我们习惯处理互斥锁和信号量 - 这些可能在低级别上使用内存屏障来实现,并且不需要明确使用内存屏障。使用内存屏障需要对硬件架构进行仔细研究,而不是应用程序代码中常见的设备驱动程序。

CPU重排序与编译器优化不同-尽管它们的效果可能相似。如果编译器重新排序您的指令可能会导致不希望的行为,需要采取单独的措施来阻止编译器重新排序(例如,在C中使用volatile关键字)。


32
我认为使用volatile关键字并不足以阻止编译器的重排优化。据我所知,它只能确保编译器无法缓存变量值。Linux内核使用gcc扩展(asm volatile("": : :"memory"))来创建完整的编译器优化屏障。 - CesarB
6
真的,volatile关键字并不会考虑线程安全,但你可以使用它来防止编译器应用某些优化 - 这与内存栅栏(fences)无关 ;) - Gwaredd
3
(.NET CLR)volatile读取操作是获取屏障,写入操作是释放屏障。Interlocked操作和MemoryBarrier方法都是完整的。 - Luke Puplett
3
在这里可以找到关于在.NET中使用volatile关键字的有趣阅读材料:http://www.albahari.com/threading/part4.aspx#_NonBlockingSynch。该网站包含了许多关于C#线程的有用信息。 - Bas Smit
@Gwaredd:你所说的“硬件执行指令完整性”的意思是什么?你能详细解释一下吗? - Nawaz
显示剩余6条评论

19

我的答案复制到另一个问题中,有哪些处理器用于优化代码的技巧?

最重要的是内存访问重排序。

在缺少内存栅栏或序列化指令的情况下,处理器可以自由地重新排序内存访问。一些处理器体系结构对它们可以重新排序的限制有所不同;Alpha被认为是最弱的(即可以重新排序最多的处理器)。

该主题的非常好的论述可以在Linux内核源文档中找到,位于Documentation/memory-barriers.txt

大多数时候,最好使用来自编译器或标准库的锁原语;这些经过充分测试,应该具有所有必要的内存屏障,并且可能已经相当优化了(优化锁定原语很棘手;即使专家有时也会出错)。


它如何影响重新排序的流程?当你说“Alpha以最弱而闻名”时,为什么是“最弱”?难道不是更好的重新排序,以便结果会更快地执行吗?(我不是Alpha用户,但询问“非常重新排序”与“受限重新排序”的影响)。那么,很多重新排序的缺点是什么(除了未定义行为的风险之外,但我想,大多数现代CPU应该已经解决了良好的重新排序,并且已经实现了只有定义的重新排序,否则,他们做出的决定就没有意义)。 - Herdsman

12

根据我的经验,它指的是一个内存屏障(memory barrier),它是一种同步多个线程之间内存访问的指令(显式或隐式)。

问题出现在现代的激进编译器和多核CPU的组合上。(编译器有极大的自由度来重新排列指令,但通常不了解你的线程)

关于这个问题的一个很好的介绍是 "'Double-Checked Locking is Broken' 声明"。对于许多人来说,这是他们开始意识到存在风险的警钟。

通常包含隐式的完整内存屏障在平台线程同步例程中,这涵盖了核心部分。然而,在无锁编程和实现自定义、轻量级同步模式时,您通常只需要该屏障,或者甚至只需要单向屏障。


2

Wikipedia 知道一切...

内存屏障(memory barrier)也被称为membar或memory fence,是一类指令,它使得中央处理器(CPU)对在屏障指令之前和之后发出的内存操作施加排序约束。

CPU使用性能优化可以导致乱序执行,包括内存加载和存储操作。内存操作重新排序通常在单个线程执行中不会被注意到,但除非仔细控制,否则会在并发程序和设备驱动程序中引起不可预测的行为。排序约束的确切性质因硬件而异,并由体系结构的内存模型定义。有些体系结构提供多个屏障以强制执行不同的排序约束。

内存屏障通常用于实现基于内存的机器码,其操作由多个设备共享。这种代码包括在多处理器系统上的同步原语和无锁数据结构,以及与计算机硬件通信的设备驱动程序。


0

内存屏障(内存栅栏)是一种无锁机制,用于同步多线程。在单线程环境中,重排序是安全的。

问题在于排序、共享资源和缓存。处理器或编译器能够为了优化而重新排序程序指令(程序员顺序)。这会在多线程环境中产生副作用。这就是为什么引入了内存栅栏来保证程序正常工作。它速度较慢,但可以解决这种类型的问题。

[Java Happens-before]

[iOS Memory Barriers]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接