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