我读过很多关于双重检查锁定的危险的文章,我会尽力避免它,但话虽如此,我认为他们读起来非常有趣。
我正在阅读 Joe Duffy 的这篇关于使用双重检查锁定实现单例的文章:http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx
他提出的解决方案(变体)似乎是这样的:
class Singleton {
private static object slock = new object();
private static Singleton instance;
private static int initialized;
private Singleton() {}
public Instance {
get {
if (Thread.VolatileRead(ref initialized) == 0) {
lock (slock) {
if (initialized == 0) {
instance = new Singleton();
initialized = 1;
}
}
}
return instance;
}
}
}
我的问题是,这是否仍然存在写入被重新排序的危险?具体是这两行:
instance = new Singleton();
initialized = 1;
如果这些写入被反转,那么其他一些线程仍然可以读取 null。
我认为关键在于链接的文章(http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S5 http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S5)。具体来说,MS 实现的 .NET 2.0 内存模型具有以下属性:
写入操作不能超过同一线程的其他写入操作。
Duffy 提到,为了在 IA-64 上支持这一点,我们做了很多工作:
我们通过 st.rel 指令确保写入在 IA-64 上具有“释放”语义来实现这一点。单个 st.rel x 保证导致其执行(在物理指令流中)的任何其他加载和存储至少在 x 的新值对另一个逻辑处理器可见时似乎已经发生在每个逻辑处理器上。加载可以被赋予“获取”语义(通过 ld.acq 指令),这意味着在 ld.acq x 之后发生的任何其他加载和存储不能出现在加载之前发生。
请注意,Duffy 还提到这是 MS 特定的保证 - 它不是 ECMA 规范的一部分(至少截至 2006 年撰写该文章)。所以,Mono 可能没那么好。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)