Страницы

Поиск по вопросам

пятница, 21 февраля 2020 г.

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

#cpp #многопоточность


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


Ответы

Ответ 1



нет, не является. Дело в том, что даже в этом случае можно сделать "обход". Детально расписано на хабре. Суть в том, что мы думаем, что в коде, одна строка атомарна, а это далеко не так. template T& single() { static T* pt; if (pt == 0) // первая проверка, вне мьютекса { StaticLock lock; if (pt == 0) // вторая проверка, под мьютексом pt = new T; // вот здесь на самом деле две строки. } return *pt; } создание объекта это обычно две операции - собственно выделение память и вызов конструктора. И в этом случае может так оказаться, что память уже выделили, а по адресу ещё не создали объект. И самая первая проверка теперь отработает по другому.

Ответ 2



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

Ответ 3



Может вы уже видели эту статью, если "гуглили" :), но там указано: Если вы укажете перед переменной volatile, то оптимизиации не будет. Хотя, использовать volotile для доступа к переменным из разных потоков - не корректно , т.к. предназначено оно для для работы с memory mapped Подробнее: Описание volatile c++ P.S. Если я правильно понял вопрос.

Ответ 4



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

Комментариев нет:

Отправить комментарий