为什么这种双重检查锁定正确?(.净)

rya*_*ner 7 c# concurrency singleton locking thread-safety

我已经阅读了很多关于双重检查锁定的危险,我会努力远离它,但据说说我认为它们是一个非常有趣的阅读.

我正在阅读Joe Duffy关于使用双重检查锁定实现单例的这篇文章: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;
    }
}
Run Code Online (Sandbox Code Playgroud)

}

我的问题是,是否仍然存在写入重新排序的危险?具体来说这两行:

instance = new Singleton();
initialized = 1;
Run Code Online (Sandbox Code Playgroud)

如果这些写入被反转,那么其他一些线程仍然可以读取null.

Mic*_*urr 5

我认为关键在于链接文章(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的新值对另一个逻辑处理器可见时,每个逻辑处理器似乎必须出现导致其执行的任何其他加载和存储(在物理指令流中).可以为LOAD赋予'acquire'语义(通过ld.acq指令),这意味着在ld.acq x之后出现的任何其他加载和存储似乎都不会在加载之前发生.

请注意,Duffy还提到这是MS特定的保证 - 它不是ECMA规范的一部分(至少在2006年的文章撰写时).所以,Mono可能不那么好.