当设计一个引用另一个对象的类时,只在第一次使用它时创建被引用的对象可能是有益的,例如使用延迟加载。
我经常使用这种模式来创建一个延迟加载的属性:
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
例如,可以创建多个对象。Encoding
我完全明白这一点,并且知道如果创建了一个额外的对象,这不是问题。它是不可变的,很快就会被垃圾收集。
但是,我真的很想了解为什么Thread.MemoryBarrier
有必要以及第二个实现与多线程场景中的第一个实现有何不同。
显然,如果线程安全是一个问题,最好的实现可能是使用Lazy<T>
:
Lazy<Encoding> lazyUtf8NoBomEncoding =
new Lazy<Encoding>(() => new UTF8Encoding(false));
Encoding Utf8NoBomEncoding {
get {
return this.lazyUtf8NoBomEncoding.Value;
}
}