尝试了解何时将锁定静态变量视为最佳实践。静态实例设置器是线程安全的吗?如果不是,应该是,为什么(不使其成为线程安全的后果是什么?)?
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;
}
}
答案 0 :(得分:3)
这称为双重检查锁定。
但是,双重检查锁定要求基础字段为volatile
1 。
简而言之,分配是原子的,但是需要在不同的内核/ 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;
}
}
}
注意 :是的,这将导致性能下降
参考
3 来自@user2864740
其他资源
答案 1 :(得分:1)
在我看来,(在setter上)锁定还是不锁定,您总是会遇到时间问题。想象一下这些情况:
无论有锁还是无锁,调用者接收哪个实例的时间都是一个问题。
我唯一看到的问题是您是否希望将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;
}