所以同时我们知道双重检查锁定在C ++中不起作用,至少不是以可移植的方式。
我刚刚意识到我在一个懒惰的四叉树中有一个脆弱的实现,我用它来制作地形光线跟踪器。所以我试图找到一种方法仍然以安全的方式使用延迟初始化,因为我不希望将内存使用量翻两番并重新排序已实现算法的大部分内容。
此遍历受C++ and the Perils of Double-Checked Locking第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;
}
}
}
}
答案 0 :(得分:3)
您的模式似乎不正确。考虑线程#1执行到第一个#pragma flush
之后的情况。然后控件切换到线程#2,线程继续并创建c
,控件在第二个#pragma flush
之前被取回。现在第一个线程醒来,重新创建孩子。
编辑:对不起,错了:它将无法锁定。
编辑2:不,仍然正确,因为该值不会在线程#1中刷新