我应该在使用setter时何时在多线程单例中锁定静态实例?

问题描述 投票:1回答:2

试图了解什么时候被认为是锁定静态变量的最佳实践。静态实例设置器是否是线程安全的?如果没有,它应该是什么以及为什么(不使它成为线程安全的结果)?

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# multithreading thread-safety
2个回答
3
投票

这称为双重检查锁定。

但是,双重检查锁定要求底层字段为volatile1。

简而言之,赋值是原子的,但需要在不同的内核/ CPU之间进行同步(完全围栏,通过锁定)。之所以另一个核心同时读取该值可能会得到一个过时的值cached1。

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

  • 避免双重检查锁定,只需在lock语句中执行所有操作。
  • 使用volatile关键字使字段变为volatile。
  • 使用Lazy类,保证是线程安全的。

注意:完全无人看守的setter进一步增加了一个并发症3 ..

但是,在你的情况下,使用双重检查锁定可能会很好地使用volatile字段进行单次检查和锁定,但我认为你最好的选择是完全lock一切都安全

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

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

注意:是的,它会导致性能下降


参考


其他资源


1
投票

在我看来,锁定或没有锁定(在二传手上),你总会有一个时间问题。想象一下这些场景:

  1. 你对setter有一个锁定,但是在锁定之前就会调用getter。调用者获取旧实例。
  2. 你对setter有一个锁定,但是在锁定之后就会调用getter。调用者等待锁定是空闲的,然后获取新实例。
  3. 您没有锁定设置器,并且在您更换实例之前就会进行调用。调用者获取旧实例。
  4. 您没有锁定设置器,并在更换实例后立即调用。调用者获取新实例。

对于锁和没有锁,这是一个时间问题,调用者接收的实例。

我能看到的唯一问题是你是否希望能够将Instance设置为null。如果是这种情况,您的当前代码将无法正常工作,因为_instance可以在if语句之间更改并返回它。您可以通过获取参考的副本来解决此问题:

public static MyClass Instance
{
    get
    {
        var instanceSafeRef = _instance;
        if(instanceSafeRef == null)
        {
            lock(_padlock)
            {
                if(_instance == null)
                {
                    _instance = new MyClass();
                }
                instanceSafeRef = _instance;
            }
        }
        return instanceSafeRef;
    }
    set => _instance = value;
}
© www.soinside.com 2019 - 2024. All rights reserved.