如果我有一个非常不可变的类型(所有成员都是只读的,如果它们是引用类型成员,那么它们也指的是不可变的对象).
我想在类型上实现一个懒惰的初始化属性,如下所示:
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;
…是线程安全的我不担心两个线程同时进行初始化,因为它将是罕见的,两个结果将从逻辑角度来看是一样的,如果我没有,我宁愿不使用锁至.
这会工作吗优缺点都有什么?
编辑:
感谢您的答案.我可能会使用锁向前移动.然而,我很惊讶,没有人提出编译器意识到temp变量是不必要的,只是直接分配给m_PropName的可能性.如果是这样,那么读取线程可能会读取一个尚未完成构造的对象.编译器是否防止这种情况?
(答案似乎表明运行时不会允许这种情况发生.)
编辑:
所以我决定用this article by Joe Duffy的启发式联锁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.