Так что между тем мы знаем, что двойной контроль-блокировка как есть, не работает на C++, по крайней мере, не в переносном режиме.Тройная проверка блокировки?
Я только что понял, что у меня хрупкая реализация в ленивом квадранте, который я использую для трассировщика луча местности. Поэтому я попытался найти способ по-прежнему использовать ленивую инициализацию безопасным образом, так как я не хотел бы увеличивать объем памяти и переупорядочивать большие части реализованных алгоритмов.
Этот обход вдохновлен рисунком на странице 12 C++ and the Perils of Double-Checked Locking, но пытается сделать это дешевле:
(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
будет также служить в качестве твердой точки последовательности, где компиляторы и процессоры выиграли» t разрешить переупорядочивать операции по ним.
Какие проблемы вы видите?
редактировать: Version 2, пытаясь учесть Влад ответа (ввести третий флеш):
(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;
}
}
}
}
}
редактировать: Version 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;
}
}
}
}
Я добавил еще одну ревизию. Хотя я должен признать, что этот день был долгим, и я медленно выхожу из мозгов. –
Я думаю, что нам все еще нужен барьер между 'create (c);' и 'childCreated [c] = true;', в противном случае они могут быть переупорядочены (и поток # 3 может начать использовать 'c', который все еще не создан) , – Vlad
после добавления третьей ревизии, которая является только дважды проверенным шаблоном, я понял именно это :) Но когда я вижу свою третью ревизию, это выглядит слишком тривиально, чтобы быть верным. И пить больше кофе больше не является вариантом, так как я уже насыщен этим:/ –