在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;
}
我只是想知道第一个内存屏障是否可以移动到 return 语句的正上方?
编辑:另一个问题:在链接的文章中,正如vidstige 所引用的
从技术上讲,您不需要完全的双向屏障。第一个障碍必须防止 Singleton 的构造向下迁移(通过另一个线程);第二个屏障必须防止 pInstance 的初始化向上迁移。这些被称为“获取”和“释放”操作,并且可能会产生比硬件(例如Itainum)上的完全障碍更好的性能,从而做出区分。
它说第二个屏障不需要是双向的,那么它如何防止对 pInstance 的分配在该屏障之前移动?尽管第一个屏障可以阻止向上迁移,但另一个线程仍然有机会看到未初始化的成员。
编辑:我想我几乎理解第一个障碍的目的。正如sonicoder 所指出的,当 if 返回 true 时,分支预测可能会导致 tmp 为 NULL。为了避免这个问题,必须有一个获取屏障来防止在读取 if 之前读取 tmp 作为返回值。
第一个屏障与第二个屏障配对以实现同步关系,因此可以向下移动。
编辑:对于那些对此问题感兴趣的人,我强烈建议阅读memory-barriers.txt。