因此,与此同时,我们知道双重检查锁定在 C++ 中不起作用,至少不能以可移植的方式工作。
我刚刚意识到我在用于地形光线追踪器的惰性四叉树中有一个脆弱的实现。所以我试图找到一种仍然以安全的方式使用延迟初始化的方法,因为我不想将内存使用量翻两番并重新排序大部分已实现的算法。
这种遍历的灵感来自C++ 第 12 页上的模式和双重检查锁定的风险,但尝试以更便宜的方式进行:
(pseudo code!)
struct Foo {
bool childCreated[4];
Mutex mutex[4];
Foo child[4];
void traverse (...) {
...
if (!childCreated[c]) {
// get updated view
#pragma flush childCreated[c]
if (!childCreated[c]) {
ScopedLock sl (mutex[c]);
if (!childCreated[c]) {
create (c);
#pragma flush childCreated[c]
childCreated[c] = true;
}
}
}
}
}
假设这#pragma flush
也将作为一个硬序列点,不允许编译器和处理器在它们之间重新排序操作。
你看到了哪些问题?
编辑:版本 2,试图考虑 Vlads 的回答(引入第三次刷新):
(pseudo code!)
struct Foo {
bool childCreated[4];
Mutex mutex[4];
Foo child[4];
void traverse (...) {
...
if (!childCreated[c]) {
// get updated view
#pragma flush childCreated[c]
if (!childCreated[c]) {
ScopedLock sl (mutex[c]);
#pragma flush childCreated[c]
if (!childCreated[c]) {
create (c);
#pragma flush childCreated[c]
childCreated[c] = true;
}
}
}
}
}
编辑:版本 3,不知何故,我发现这与版本 2 相当,因为我没有使用孩子本身,而是使用原始标志来检查有效性,基本上依赖于创建孩子和写入该标志之间的内存屏障。
(pseudo code!)
struct Foo {
bool childCreated[4];
Mutex mutex[4];
Foo child[4];
void traverse (...) {
...
if (!childCreated[c]) {
ScopedLock sl (mutex[c]);
#pragma flush childCreated[c]
if (!childCreated[c]) {
create (c);
#pragma flush childCreated[c]
childCreated[c] = true;
}
}
}
}