.net - 有人可以简单解释一下如何使用 Threading.MemoryBarrier 在 .Net 中实现 'Full Fences' 吗?

标签 .net multithreading thread-safety jit

我很清楚 MemoryBarrier 的用法,但不清楚运行时幕后发生的事情。任何人都可以对发生的事情做出很好的解释吗?

最佳答案

在真正强大的内存模型中,不需要发出栅栏指令。所有内存访问将按顺序执行,所有存储将全局可见。

需要内存栅栏,因为当前常见的架构 do not provide a strong memory model - 例如,x86/x64 可以相对于写入重新排序读取。 (更全面的来源是 "Intel® 64 and IA-32 Architectures Software Developer’s Manual ,8.2.2 P6 和更多最新处理器系列中的内存排序”)。作为来自无数的例子,Dekker's algorithm will fail on x86/x64 没有围栏。

即使 JIT 生成的机器代码中小心放置了带有内存加载和存储的指令,如果 CPU 然后重新排序这些加载和存储,它的努力也是无用的——只要保持当前顺序一致性的错觉,它就可以做到。上下文/线程。

冒着过于简单化的风险:将指令流产生的负载和存储可视化为一群雷鸣般的野生动物可能会有所帮助。
当它们穿过狭窄的桥梁(您的 CPU)时,您永远无法确定动物的顺序,因为它们中的一些会更慢,一些会更快,一些会超车,一些会落后。
如果在开始时——当你发出机器代码时——你通过在它们之间放置无限长的栅栏将它们分成几组,你至少可以确保 A 组在 B 组之前。

栅栏确保读取和写入的顺序。措辞并不准确,但:

  • 存储栅栏“等待”所有未完成的存储(写入)操作完成,但不影响加载。
  • 加载栅栏“等待”所有未完成的加载(读取)操作完成,但不影响存储。
  • 一个完整的围栏“等待”所有存储和加载操作完成。它的效果是,在栅栏之前的读取和写入将在“栅栏的另一边”(迟于栅栏)的写入和加载之前执行。

  • JIT 为完整栅栏发出的内容取决于(CPU)架构以及它提供的内存排序保证。
    由于 JIT 确切地知道它在什么架构上运行,它可以发出正确的指令。

    在我的 x64 机器上,使用 .NET 4.0 RC,它恰好是 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 ,对于两个是 might result in different behavior )。 MFENCE和它的 friend 可以在第 8.2.5 章“加强或削弱内存排序模型”中找到。

  • 还有更多的方法可以序列化存储和加载,但它们要么不切实际,要么比上述方法慢:
  • 在第 8.3 章中,您可以找到完整的序列化指令,例如 CPUID .这些序列化指令流也是:“没有什么可以传递序列化指令和
    序列化指令不能通过任何其他指令(读、写、指令
    取或 I/O)”。
  • 如果您将内存设置为强未缓存 (UC),it will give you a strong memory model : 不允许任何推测或乱序访问,所有访问都将出现在总线上,因此无需发出指令。 :) 当然,这会比平时慢一点。

  • ...

    所以这取决于。如果有一台具有强大排序保证的计算机,那么 JIT 可能不会发出任何信息。

    IA64 和其他架构有自己的内存模型——因此保证内存排序(或没有它们)——以及他们自己的指令/方法来处理内存存储/加载排序。

    关于.net - 有人可以简单解释一下如何使用 Threading.MemoryBarrier 在 .Net 中实现 'Full Fences' 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2494057/

    相关文章:

    java - 线程中的 NoClassDefFoundError

    c++ - C/C++中的 `_dl_init`(程序初始化)执行是单线程的吗?

    C#程序导致蓝屏?

    .net - 负载测试调试不起作用

    c# - 必须在与 DependencyObject 相同的线程上创建 DependencySource

    c# - 如何通过鼠标和按键事件确定 TreeView 上的节点

    multithreading - 所有goroutines死锁

    c++ - 模型、 View 、 Controller ——我理解类图,但我不理解所有的线程问题。建议?

    c# - 监听依赖属性的变化

    .net - Visual Studio 2010 ADO.NET 数据模型与 Linq to SQL