编程语言
首页 > 编程语言> > c# – 延迟加载和使用Thread.MemoryBarrier

c# – 延迟加载和使用Thread.MemoryBarrier

作者:互联网

在设计具有对另一个对象的引用的类时,仅在第一次使用时创建引用的对象可能是有益的,例如,使用延迟加载.

我经常使用这种模式来创建一个延迟加载的属性:

Encoding utf8NoBomEncoding;

Encoding Utf8NoBomEncoding {
  get {
    return this.utf8NoBomEncoding ?? 
      (this.utf8NoBomEncoding = new UTF8Encoding(false));
  }
}

然后我在浏览BCL的源代码时遇到了这段代码:

Encoding Utf8NoBomEncoding {
  get {
    if (this.utf8NoBomEncoding == null) {
      var encoding = new UTF8Encoding(false);
      Thread.MemoryBarrier();
      this.utf8NoBomEncoding = encoding;
    }
    return this.utf8NoBomEncoding;
  }
}

据我所知,这些都不是线程安全的.例如.可以创建多个编码对象.我完全明白了,如果创建了额外的Encoding对象,那就不是问题了.它是不可变的,很快就会被垃圾收集.

但是,我真的很想知道为什么Thread.MemoryBarrier是必要的,以及第二个实现与多线程场景中的第一个实现有何不同.

显然,如果线程安全是一个问题,最好的实现可能是使用Lazy< T>:

Lazy<Encoding> lazyUtf8NoBomEncoding = 
  new Lazy<Encoding>(() => new UTF8Encoding(false));

Encoding Utf8NoBomEncoding {
  get {
    return this.lazyUtf8NoBomEncoding.Value;
  }
}

解决方法:

这段代码将是一场没有内存障碍的灾难.仔细看看这些代码行.

  var encoding = new UTF8Encoding(false);
  Thread.MemoryBarrier();
  this.utf8NoBomEncoding = encoding;

现在,想象一些其他线程看到最后一行的效果,但没有看到第一行的效果.那将是一场彻底的灾难.

内存屏障确保任何看到编码的线程都能看到其构造函数的所有效果.

例如,没有内存屏障,第一行和最后一行可以在内部优化(大致)如下:
1)分配一些内存,在this.utf8NoBomEncoding中存储指向它的指针
2)调用UTF8Encoding构造函数以使用有效值填充该内存.

想象一下,如果在步骤1和步骤2之间运行另一个线程并通过此代码.它将使用尚未构建的对象.

标签:c,net,multithreading,lazy-loading,memory-barriers
来源: https://codeday.me/bug/20190610/1209250.html