试图了解什么时候被认为是锁定静态变量的最佳实践。静态实例设置器是否是线程安全的?如果没有,它应该是什么以及为什么(不使它成为线程安全的结果)?
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;
}
}
这称为双重检查锁定。
但是,双重检查锁定要求底层字段为volatile
1。
简而言之,赋值是原子的,但需要在不同的内核/ 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;
}
}
}
注意:是的,它会导致性能下降
参考
其他资源
在我看来,锁定或没有锁定(在二传手上),你总会有一个时间问题。想象一下这些场景:
对于锁和没有锁,这是一个时间问题,调用者接收的实例。
我能看到的唯一问题是你是否希望能够将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;
}