如果我有一个非常不可变的类型(所有成员都是只读的,如果它们是引用类型成员,那么它们也指的是非常不可变的对象)。
我想在类型上实现一个惰性初始化属性,如下所示:
private ReadOnlyCollection<SomeImmutableType> m_PropName = null;
public ReadOnlyCollection<SomeImmutableType> PropName
{
get
{
if(null == m_PropName)
{
ReadOnlyCollection<SomeImmutableType> temp = /* do lazy init */;
m_PropName = temp;
}
return m_PropName;
}
}
据我所知:
m_PropName = temp;
...是线程安全的。我不太担心两个线程都竞相同时初始化,因为这种情况很少见,从逻辑角度来看,两个结果都是相同的,如果我没有,我宁愿不使用锁到。
这行得通吗?优缺点都有什么?
编辑: 感谢您的回答。我可能会继续使用锁。然而,令我惊讶的是,没有人提出编译器意识到临时变量是不必要的,只是直接分配给 m_PropName 的可能性。如果是这种情况,那么读取线程可能会读取尚未完成构造的对象。编译器会阻止这种情况吗?
(答案似乎表明运行时不允许这种情况发生。)
编辑: 所以我决定采用受 Joe Duffy 的这篇文章启发的 Interlocked CompareExchange 方法。
基本上:
private ReadOnlyCollection<SomeImmutableType> m_PropName = null;
public ReadOnlyCollection<SomeImmutableType> PropName
{
get
{
if(null == m_PropName)
{
ReadOnlyCollection<SomeImmutableType> temp = /* do lazy init */;
System.Threading.Interlocked(ref m_PropName, temp, null);
}
return m_PropName;
}
}
这应该确保在此对象实例上调用此方法的所有线程都将获得对同一对象的引用,因此 == 运算符将起作用。可能会浪费工作,这很好 - 它只是使它成为一个乐观的算法。
正如下面的一些评论所指出的,这取决于 .NET 2.0 内存模型的工作。否则,应将 m_PropName 声明为 volatile。