我很清楚MemoryBarrier的用法,但不清楚运行时幕后发生了什么。有没有人能给出一个很好的解释?
发布于 2010-03-23 03:22:59
在一个非常强大的内存模型中,不需要发出栅栏指令。所有内存访问将按顺序执行,并且所有存储将是全局可见的。
因为当前的通用架构do not provide a strong memory model - x86/x64可以例如相对于写入对读取进行重新排序,所以需要存储器栅栏。(更彻底的来源是,8.2.2 P6和更新的处理器家族中的内存排序“)。作为一个来自gazillion的例子,Dekker's algorithm will fail on x86/x64没有栅栏。
即使JIT生成的机器码中小心地放置了具有内存加载和存储的指令,如果CPU随后对这些加载和存储进行重新排序,那么JIT的努力也是徒劳的-它可以这样做,只要保持当前上下文/线程的顺序一致性的假象。
冒着过于简单化的风险:它可能有助于将指令流产生的加载和存储可视化为一群雷鸣般的野生动物。当它们穿过一座狭窄的桥(你的CPU)时,你永远不能确定动物的顺序,因为它们中的一些会慢一些,一些会快一些,一些会超车,一些会落后。如果在开始时-当您发出机器代码时-通过在它们之间放置无限长的栅栏来将它们划分为组,您至少可以确保组A在组B之前。
栅栏可确保读取和写入的顺序。措辞不准确,但:
JIT为完全隔离发出的内容取决于(CPU)体系结构以及它所提供的内存排序保证。因为JIT确切地知道它在什么架构上运行,所以它可以发出适当的指令。
在我的安装了.NET 4.0RC的x64机器上,它恰好是一个lock or
。
int a = 0;
00000000 sub rsp,28h
Thread.MemoryBarrier();
00000004 lock or dword ptr [rsp],0
Console.WriteLine(a);
00000009 mov ecx,1
0000000e call FFFFFFFFEFB45AB0
00000013 nop
00000014 add rsp,28h
00000018 ret
Intel® 64 and IA-32 Architectures Software Developer’s Manual第8.1.2章:
锁定操作“
MFENCE
可以用作完全屏障(至少在理论上是这样的--一个是locked operations might be faster,两个是it might result in different behavior)。MFENCE
和它的朋友可以在8.2.5“加强或削弱记忆排序模型”一章中找到。有更多的方法来序列化存储和加载,尽管它们要么不切实际,要么比上面的方法慢:
CPUID
。这些序列化指令流:“任何东西都不能传递序列化指令,而序列化指令不能传递任何其他指令(读、写、取指令或I/O)”。..。
所以这取决于。如果有一台具有强大排序保证的计算机,JIT很可能什么也不会发出。
IA64和其他架构都有自己的内存模型--因此保证内存排序(或不保证内存排序)--以及它们自己的指令/方法来处理内存存储/加载排序。
发布于 2010-03-23 01:49:58
在进行无锁并发编程时,应该注意程序指令的重新排序。
程序指令的重新排序可以在以下几个阶段发生:
optimizations
内存栅栏是确保程序指令特定顺序的唯一方法。基本上,内存围栏是一类使CPU强制执行排序约束的指令。记忆栅栏可以分为三类:
在.NET框架中,有很多种方法可以发射栅栏:互锁、监视器、ReaderWriterLockSlim等。
Thread.MemoryBarrier在即时编译器和处理器级别上都是完全隔离的。
https://stackoverflow.com/questions/2494057
复制相似问题