三重检查锁定? 所以,我们知道,双检查锁是不工作的C++,至少不是以便携的方式。
我刚刚意识到我在一个懒惰的四叉树中有一个脆弱的实现,我将它用于地形光线跟踪器。因此,我试图找到一种方法,仍然以安全的方式使用延迟初始化,因为我不想将内存使用率提高四倍,并重新排序大部分已实现的算法 此遍历的灵感来源于的第12页上的模式,但尝试以更便宜的价格进行:三重检查锁定? 所以,我们知道,双检查锁是不工作的C++,至少不是以便携的方式。,c++,c,multicore,double-checked-locking,lazy-initialization,C++,C,Multicore,Double Checked Locking,Lazy Initialization,我刚刚意识到我在一个懒惰的四叉树中有一个脆弱的实现,我将它用于地形光线跟踪器。因此,我试图找到一种方法,仍然以安全的方式使用延迟初始化,因为我不想将内存使用率提高四倍,并重新排序大部分已实现的算法 此遍历的灵感来源于的第12页上的模式,但尝试以更便宜的价格进行: (pseudo code!) struct Foo { bool childCreated[4]; Mutex mutex[4]; Foo child[4]; void traverse (...)
(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答案(引入第三次刷新):
edit:Version 3,我不知何故发现这相当于Version 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;
}
}
}
}
看来你的模式不正确。考虑线程1执行到第一个代码> >然后控件切换到线程#2,该线程继续并创建一个
c
,控件在第二个#pragma flush
之前被取回。现在第一个线程醒来,并重新创建子线程
编辑:抱歉,错误:它将无法获取锁
编辑2:不,仍然正确,因为该值不会在线程1中刷新我添加了另一个修订。虽然我必须承认,这一天已经很长了,我已经慢慢摆脱了头脑风暴。我认为我们仍然需要在
create(c)代码>和childCreated[c]=true代码>,否则它们可能会被重新排序(线程#3可能会开始使用尚未创建的c
)。在添加第三个版本时,我完全意识到:),但当我看到第三个版本时,它看起来太琐碎而不正确。喝更多的咖啡不再是一种选择,因为我已经对此饱和了:/你的修订版#3是否与你提到的PDF第12页的代码基本相同?我认为在输入函数和第一次检查之间存在障碍,但我的第一个障碍是在第一次检查之后。
(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;
}
}
}
}