对深度不可变类型进行延迟初始化是否需要锁?

2024-01-09

如果我有一个深度不可变的类型(所有成员都是只读的,如果它们是引用类型成员,那么它们也引用深度不可变的对象)。

我想在类型上实现一个延迟初始化的属性,如下所示:

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; 

...是线程安全的。我不太担心两个线程同时竞争初始化,因为这种情况很少见,从逻辑角度来看,两个结果都是相同的,如果没有锁,我宁愿不使用锁到。

这行得通吗?优缺点都有什么?

Edit:感谢您的回答。我可能会继续使用锁。然而,令我惊讶的是没有人提出编译器意识到 temp 变量是不必要的,并且直接分配给 m_PropName 的可能性。如果是这种情况,那么读取线程可能会读取尚未完成构造的对象。编译器会阻止这种情况吗?

(答案似乎表明运行时不允许这种情况发生。)

Edit:因此,我决定采用 Interlocked CompareExchange 方法,其灵感来自乔·达菲的这篇文章 https://web.archive.org/web/20080216100813/http://www.bluebytesoftware.com/blog/2007/11/17/ImmutableTypesCanCopyTheWorldSafely.aspx.

基本上:

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 应该被声明为易失性的。


那可行。在 C# 中写入引用保证是原子的,如 5.5 节所述spec http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx。 这可能仍然不是一个好方法,因为您的代码在调试和读取时会更加混乱,而对性能的影响可能很小。

乔恩·斯基特有一个伟大的page http://www.yoda.arachsys.com/csharp/singleton.html关于在 C# 中实现单例。

关于此类小型优化的一般建议是不要执行这些优化,除非探查器告诉您此代码是热点。另外,您应该警惕编写大多数程序员在不检查规范的情况下无法完全理解的代码。

编辑:正如评论中所指出的,即使您说您不介意创建对象的两个版本,但这种情况非常违反直觉,因此永远不应该使用这种方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对深度不可变类型进行延迟初始化是否需要锁? 的相关文章

随机推荐