In ,有伪代码来正确实现作者建议的模式。见下文,
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 语句的正上方?
EDIT:另一个问题:在链接的文章中,如vidstige https://stackoverflow.com/users/363437/vidstige quoted
从技术上讲,您不需要完全的双向屏障。第一个障碍
必须防止 Singleton 构造向下迁移
(通过另一个线程);第二个障碍必须防止向上迁移
pInstance 的初始化。这些被称为“获取”并且
“释放”操作,可能会产生比完整操作更好的性能
硬件(例如 Itanum)上的障碍造成了区别。
它说第二个屏障不需要是双向的,那么它如何防止对 pInstance 的分配移动到该屏障之前呢?尽管第一个屏障可以阻止向上迁移,但另一个线程仍然有机会看到未初始化的成员。
EDIT:我想我差不多明白第一道屏障的目的了。作为声波编码器 https://stackoverflow.com/users/315144/sonicoder请注意,当 if 返回 true 时,分支预测可能会导致 tmp 为 NULL。为了避免这个问题,必须有一个获取屏障来防止在读入 if 之前返回读取 tmp。 罢工>
第一屏障与第二屏障配对以实现同步关系,所以可以下移。
EDIT: 对于这个问题感兴趣的人,我强烈建议阅读内存屏障.txt http://www.kernel.org/doc/Documentation/memory-barriers.txt.
我在这里没有看到任何与你的问题相关的正确答案,所以我决定在三年多后发布一个;)
我只是想知道第一个内存屏障是否可以向右移动
返回语句上方?
是的,它可以。
这是针对不会进入的线程if
声明,即pInstance
已经被正确构造和初始化,并且是可见的。
第二个障碍(之前的那个pInstance = tmp;
) 保证单例成员字段的初始化在之前提交到内存pInstance = tmp;
承诺。但这并不一定意味着其他线程(在其他内核上)会以相同的顺序看到这些内存效应(违反直觉,对吧?)。第二个线程可能会看到缓存中指针的新值,但还看不到那些成员字段。当它通过取消引用指针来访问成员时(例如,p->data
),该成员的地址可能已经在缓存中,但不是所需的地址。砰!读取了错误的数据。请注意,这不仅仅是理论上的。有系统 http://www.cs.umd.edu/~pugh/java/memoryModel/AlphaReordering.html您需要执行缓存一致性指令(例如内存屏障)以从内存中提取新数据。
这就是为什么第一个障碍存在。它还解释了为什么可以将其放在前面return
声明(但必须在Singleton* tmp = pInstance;
).
它说第二个障碍不需要是双向的,所以
如何防止 pInstance 的分配之前被移动
那个障碍?
写屏障保证它之前的每个写入实际上都会在其之后的每个写入之前发生。这是一个停止标志,任何写入都不能越过它到达另一边。更详细的描述,请参考here http://www.mjmwired.net/kernel/Documentation/memory-barriers.txt#305.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)