Блокировка с двойной проверкой

257
24 апреля 2017, 04:47

В книге Энтони Уильямса "Параллельное программирование на C++ в действии" рекомендуется заменять использование упомянутого в заголовке паттерна использованием std::call_once. Немного погуглив, обнаружил, что в других языках, например, Java и C# подобная проблема решается объявление переменной как volatile для предотвращение оптимизаций компилятором. Является ли использование volatileрешением и в C++ ? Или же здесь она работает как-то иначе?

Answer 1

нет, не является. Дело в том, что даже в этом случае можно сделать "обход". Детально расписано на хабре.

Суть в том, что мы думаем, что в коде, одна строка атомарна, а это далеко не так.

template<typename T>
T& single()
{
    static T* pt;
    if (pt == 0)      // первая проверка, вне мьютекса
    {
        StaticLock lock;
        if (pt == 0)  // вторая проверка, под мьютексом
            pt = new T; // вот здесь на самом деле две строки.
    }
    return *pt;
}

создание объекта это обычно две операции - собственно выделение память и вызов конструктора. И в этом случае может так оказаться, что память уже выделили, а по адресу ещё не создали объект. И самая первая проверка теперь отработает по другому.

Answer 2

Может вы уже видели эту статью, если "гуглили" :), но там указано:

Если вы укажете перед переменной volatile, то оптимизиации не будет.

Хотя, использовать volotile для доступа к переменным из разных потоков - не корректно , т.к. предназначено оно для для работы с memory mapped

Подробнее: Описание volatile c++

P.S. Если я правильно понял вопрос.

Answer 3

Мейерс пишет, что volatile применяется для памяти, чтения и записи которой не должны удаляться при оптимизации, и что надо четко отличать volatile и std::atomic, который применяется для обращения нескольких потоков к данным без использования мьютексов.

Если это не в тему - извините :(

Answer 4

В случае MSVC, под volatile потокобезопасность подразумевается (за исключением архитектуры ARM). Конкретно, MSVC генерирует дополнительный исполняемый код для обеспечения особого порядка доступа к переменной из разных потоков. Такое поведение задается ключом компилятора /volatile:ms.

Напротив ключ /volatile:iso задает поведение по стандарту, то есть квалификатор volatile отключает оптимизации и к потокобезопасности явного отношения не имеет.

При этом по умолчанию, в MSVC подразумевается /volatile:ms, то есть поведение MSVC в отношении volatile не является стандартным по умолчанию!

READ ALSO
контейнер map c++

контейнер map c++

Как использовать контейнер map, для хранения счетчиков?

210
Вывести структуру на экран

Вывести структуру на экран

имеется вот такой код

275
sizeof() и битовые поля

sizeof() и битовые поля

Вот имеется структура:

280
Запись структуры в файл с++ Visual Studio

Запись структуры в файл с++ Visual Studio

Какая -то фигняОшибок при компиляции не выдает, но не работает

433