Тройное проверенное блокировка?
-
24-09-2019 - |
Вопрос
Таким образом, в то же время мы знаем, что двойная проверка, как это не работает в C ++, по крайней мере, не портативным способом.
Я только что понял, что у меня есть хрупкая реализация в ленивом квадрете, который я использую для трассера лучей местности. Поэтому я попытался найти способ до сих пор безопасным образом использовать ленивую инициализацию, так как я не хотел бы четырехместное использование памяти и заказать большие части реализованных алгоритмов.
Этот обход вдохновлен шаблоном на стр. 12 C ++ и опасности двойной проверки, но пытается сделать это дешевле:
(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;
}
}
}
}
Решение
Похоже, ваша картина не правильная. Рассмотрим случай, когда нить № 1 выполняется до первого #pragma flush
. Отказ Затем управление переключательными к потоку № 2, которое продолжается и создает c
, контроль занесен только до секунды #pragma flush
. Отказ Теперь первый нить просыпается и создает ребенка заново.
Редактировать: Извините, неправильно: он не сможет взять замок.
Отредактируйте 2: Нет, все еще исправьте, потому что значение будет не промытым в потоке # 1