首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >有人能简单解释一下如何使用Threading.MemoryBarrier在.Net中实现“全围栏”吗?

有人能简单解释一下如何使用Threading.MemoryBarrier在.Net中实现“全围栏”吗?
EN

Stack Overflow用户
提问于 2010-03-23 00:43:38
回答 2查看 2K关注 0票数 16

我很清楚MemoryBarrier的用法,但不清楚运行时幕后发生了什么。有没有人能给出一个很好的解释?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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之前。

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

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

JIT为完全隔离发出的内容取决于(CPU)体系结构以及它所提供的内存排序保证。因为JIT确切地知道它在什么架构上运行,所以它可以发出适当的指令。

在我的安装了.NET 4.0RC的x64机器上,它恰好是一个lock or

代码语言:javascript
复制
            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章:

锁定操作“

  • "...locked操作序列化所有未完成的加载和存储操作(即等待它们完成)。”...“锁定操作相对于所有其他内存操作和所有外部可见事件是原子的。只有指令提取和页表访问才能传递锁定指令。锁定指令可用于同步由一个处理器写入的数据和由另一个can指令读取的数据,以满足这一特定需求。在上述情况下,MFENCE可以用作完全屏障(至少在理论上是这样的--一个是locked operations might be faster,两个是it might result in different behavior)。MFENCE和它的朋友可以在8.2.5“加强或削弱记忆排序模型”一章中找到。

有更多的方法来序列化存储和加载,尽管它们要么不切实际,要么比上面的方法慢:

  • 在8.3章中,你可以找到完整的序列化指令,比如CPUID。这些序列化指令流:“任何东西都不能传递序列化指令,而序列化指令不能传递任何其他指令(读、写、取指令或I/O)”。
  • 如果您将内存设置为强未缓存(UC),则it will give you a strong memory model:不允许任何推测性或乱序访问,并且所有访问都将出现在总线上,因此无需发出指令。:)当然,这会比平时慢一点。

..。

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

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

票数 16
EN

Stack Overflow用户

发布于 2010-03-23 01:49:58

在进行无锁并发编程时,应该注意程序指令的重新排序。

程序指令的重新排序可以在以下几个阶段发生:

optimizations

  • JIT编译器optimizations

  • CPU optimizations.

  1. C#/VB.NET/F#编译器

内存栅栏是确保程序指令特定顺序的唯一方法。基本上,内存围栏是一类使CPU强制执行排序约束的指令。记忆栅栏可以分为三类:

  1. 加载栅栏-确保没有加载CPU指令跨越栅栏
  2. 存储栅栏-确保没有存储CPU指令跨越栅栏
  3. 全栅栏-确保没有加载或存储CPU指令跨越栅栏

在.NET框架中,有很多种方法可以发射栅栏:互锁、监视器、ReaderWriterLockSlim等。

Thread.MemoryBarrier在即时编译器和处理器级别上都是完全隔离的。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2494057

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档