有一种无需volatile
现场即可实现的方法。我会解释...
我认为这是很危险的锁内部内存访问重新排序,这样您就可以在锁外部获得未完全初始化的实例。为了避免这种情况,我这样做:
public sealed class Singleton
{
private static Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
var temp = new Singleton();
System.Threading.Thread.MemoryBarrier();
instance = temp;
}
}
}
return instance;
}
}
}
了解代码
想象一下,在Singleton类的构造函数中有一些初始化代码。如果在将字段设置为新对象的地址后对这些指令进行了重新排序,则您的实例不完整...假设该类具有以下代码:
private int _value;
public int Value { get { return this._value; } }
private Singleton()
{
this._value = 1;
}
现在,假设使用new运算符调用构造函数:
instance = new Singleton();
可以将其扩展为以下操作:
ptr = allocate memory for Singleton;
set ptr._value to 1;
set Singleton.instance to ptr;
如果我像这样重新排序这些说明怎么办:
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
set ptr._value to 1;
这有什么不同吗?NO,如果你认为一个单线程的。YES,如果你认为多线程的......如果有什么刚过线程interruped set instance to ptr
:
ptr = allocate memory for Singleton;
set Singleton.instance to ptr;
-- thread interruped here, this can happen inside a lock --
set ptr._value to 1; -- Singleton.instance is not completelly initialized
这是通过不允许对内存访问进行重新排序来避免内存障碍的:
ptr = allocate memory for Singleton;
set temp to ptr;
set ptr._value to 1;
-- memory barrier... cannot reorder writes after this point, or reads before it --
-- Singleton.instance is still null --
set Singleton.instance to temp;
祝您编码愉快!