因此,与此同时,我们知道双重检查锁定在 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;
}
}
}
}
}
假设#pragmalush
也将充当硬序列点,不允许编译器和处理器在它们之间重新排序操作。
您发现哪些问题?
编辑:版本 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 相当,因为我没有使用子级本身,而是使用原始标志来检查有效性,基本上依赖于创建之间的内存屏障一个 child 并向该标志写入内容。
(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;
}
}
}
}
最佳答案
看来你的模式不正确。考虑线程#1 执行到第一个#pragmalush
之后的情况。然后控制权切换到线程 #2,该线程继续创建一个 c
,控制权在第二个 #pragmalush
之前被收回。现在第一个线程醒来,并重新创建子线程。
编辑:抱歉,错误:它将无法获取锁定。
编辑 2:不,仍然正确,因为该值不会在线程 #1 中刷新
关于c++ - 三重检查锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2372468/