首页 > 解决方案 > 使用 setter 时,何时应该在多线程单例中锁定静态实例?

问题描述

试图了解何时锁定静态变量被认为是最佳实践。静态实例设置器是线程安全的吗?如果不是,应该是,为什么(不使其成为线程安全的后果是什么)?

class MyClass
{
    private static MyClass _instance;

    private static readonly object _padlock = new object();

    public static MyClass Instance
    {
        get
        {
            if(_instance == null)
            {
                lock(_padlock)
                {
                    if(_instance == null)
                    {
                        _instance = new MyClass();
                    }
                }
            }
            return _instance;
        }
        set => _instance = value;
    }

}

标签: c#multithreadingthread-safety

解决方案


这称为双重检查锁定

但是,双重检查锁定要求基础字段为volatile1

简而言之,分配是原子的,但需要在不同的内核/CPU 之间进行同步(全围栏,通过锁)。另一个核心同时读取该值的原因可能会缓存过时的值1

有几种方法可以使代码线程安全:

  • 避免双重检查锁定,并简单地执行lock语句中的所有内容。
  • 使用关键字使字段可变。volatile
  • 使用Lazy保证是线程安全的类。

注意:完全无人看守的二传手进一步增加了复杂性3 ..

但是,在您的情况下,使用双重检查锁定可能会与单次检查和锁定volatile字段一起正常工作,但我认为您最好的选择是填满lock所有内容并确保安全

public static MyClass Instance
{
    get
    {
         lock(_padlock)
         {
             if(_instance == null)
                 _instance = new MyClass();
             return _instance;
         }

    }
    set 
    {
         lock(_padlock)
         {
             _instance = value;
         }
    } 
}

注意:是的,它会导致性能损失


参考


其他资源


推荐阅读