Вопрос

Таким образом, в то же время мы знаем, что двойная проверка, как это не работает в 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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top