2015-09-28 76 views
6

浏览MEF源代码我找到了这一块。 有人可以解释为什么在锁内需要MemoryBarrier为什么在这里使用Thread.MemoryBarrier?

整个方法是:

public void SatisfyImportsOnce(ComposablePart part) 
{ 
    this.ThrowIfDisposed(); 

    if (this._importEngine == null) 
    { 
     ImportEngine importEngine = new ImportEngine(this, this._compositionOptions); 

     lock(this._lock) 
     { 
      if (this._importEngine == null) 
      { 
       Thread.MemoryBarrier(); 
       this._importEngine = importEngine; 
       importEngine = null; 
      } 
     } 
     if(importEngine != null) 
     { 
      importEngine.Dispose(); 
     } 
    } 
    this._importEngine.SatisfyImportsOnce(part); 
} 
+0

之前就*似乎*有时候,锁不够 –

+0

这是不可能回答这个问题,不知道多了很多内容。 –

+3

这是微软内存模式的处理器上的FUD,一些微软程序员可能永远不会从驯服安腾处理器中恢复过来。它确保另一个线程在使用_importEngine引用时可以观察完全构造的对象。在弱处理器上,在写入对象字段之前可以将引用写入内存,以便另一个线程可以看到未初始化的字段值。因为.NET 2.0并不是必需的,并且在这里绝对没有必要,因为锁已经意味着内存屏障。 –

回答

1

Thread.MemoryBarrier防止抖动/编译器的任何指令重新排序代码优化。

Treading in C#, by Joe Albahari本书中,他最高审计机关:

  • 编译器,CLR,或CPU可能会重新排列你的程序的指令来提高效率。
  • 编译器,CLR或CPU可能会引入缓存优化,以便对其他线程立即无法看到变量赋值。

在这个例子中,可能会导致importEngine或_importEngine值被缓存,所有线程都必须立即通知所有线程。

在这种情况下内存屏障提供importEngine 新鲜 garantee它分配给_importEngine

+1

但锁也意味着一个内存barrior。 'importEngine'和'_importEngine'都不能在锁内缓存。 (除非其他东西在不锁定this._lock的情况下写入'_importEngine')。 –

+0

@ScottChamberlain你是对的。这就是为什么我认为在这种情况下使用MemoryBarrier的原因,可能有其他线程在不使用_lock的情况下写入_importEnginge。 –

+2

在CLI(CLR的标准化版本)中,对双重检查的锁定语言的基本空检查功能不足。 CLI的内存模型需要变量是易变的,或者使用明确的障碍。 CLR具有更强大的内存模型,既不需要波动也不需要屏障。该代码的作者可能没有意识到CLR的额外保证,或者Jon Skeet更喜欢不依赖它们。障碍成本有限,因为一旦初始化和同步,以后的调用将完全跳过锁的内容。 –