在C++ and the Perils of Double-Checked Locking中,作者举例说明了如何正确实施模式。在双重锁定的锁定模式中获取屏障
Singleton* Singleton::instance() {
Singleton* tmp = pInstance;
... // insert memory barrier (1)
if (tmp == 0) {
Lock lock;
tmp = pInstance;
if (tmp == 0) {
tmp = new Singleton;
... // insert memory barrier (2)
pInstance = tmp;
}
}
return tmp;
}
我无法弄清楚什么,不过,如果是第一个内存屏障必须Singleton* tmp = pInstance;
后? (编辑:要清楚,我知道这个障碍是必要的,我不明白的是它是否必须在分配tmp后才会出现)如果是这样,为什么?以下内容无效吗?
Singleton* Singleton::instance() {
... // insert memory barrier (1)
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
Singleton* tmp = new Singleton;
... // insert memory barrier (2)
pInstance = tmp;
}
}
return pInstance;
}
我不是在猜测什么编译器可能会做一个专家,但即使对正确性并不重要,维护'tmp'确实避免了在普通(已经初始化)的情况下重复读取全局状态;如果你的周期非常困难,那么双重检查锁定似乎是一个好主意,避免重复读取全局状态可以确保你不会以其他方式牺牲某些收益; 'tmp'是堆栈本地的(所以共享不是问题);编译器可以安全地避免重读它,但可能无法优化第二次直接读取“pInstance”。 – ShadowRanger
@ShadowRanger所以也许我的修改是有效的,但作者选择以这种方式实现它为您提到的优化?不幸的是,这篇论文并没有解释最终设计的理由,除了需要设置障碍。 – user1747505
@ user1747505您的更改不能保证将'pInstance'视为非NULL的线程可以看到底层对象的初始化。您认为哪种内存屏障可以确保这一点,考虑到将'pInstance'视为非NULL的线程永远不会遇到任何内存障碍。 –