В книге Энтони Уильямса "Параллельное программирование на C++ в действии" рекомендуется заменять использование упомянутого в заголовке паттерна использованием std::call_once. Немного погуглив, обнаружил, что в других языках, например, Java и C# подобная проблема решается объявление переменной как volatile для предотвращение оптимизаций компилятором. Является ли использование volatileрешением и в C++ ? Или же здесь она работает как-то иначе?
нет, не является. Дело в том, что даже в этом случае можно сделать "обход". Детально расписано на хабре.
Суть в том, что мы думаем, что в коде, одна строка атомарна, а это далеко не так.
template<typename T>
T& single()
{
static T* pt;
if (pt == 0) // первая проверка, вне мьютекса
{
StaticLock lock;
if (pt == 0) // вторая проверка, под мьютексом
pt = new T; // вот здесь на самом деле две строки.
}
return *pt;
}
создание объекта это обычно две операции - собственно выделение память и вызов конструктора. И в этом случае может так оказаться, что память уже выделили, а по адресу ещё не создали объект. И самая первая проверка теперь отработает по другому.
Может вы уже видели эту статью, если "гуглили" :), но там указано:
Если вы укажете перед переменной volatile, то оптимизиации не будет.
Хотя, использовать volotile для доступа к переменным из разных потоков -
не корректно , т.к. предназначено оно для для работы с memory mapped
Подробнее: Описание volatile c++
P.S. Если я правильно понял вопрос.
Мейерс пишет, что volatile применяется для памяти, чтения и записи которой не должны удаляться при оптимизации, и что надо четко отличать volatile и std::atomic, который применяется для обращения нескольких потоков к данным без использования мьютексов.
Если это не в тему - извините :(
В случае MSVC, под volatile потокобезопасность подразумевается (за исключением архитектуры ARM). Конкретно, MSVC генерирует дополнительный исполняемый код для обеспечения особого порядка доступа к переменной из разных потоков. Такое поведение задается ключом компилятора /volatile:ms.
Напротив ключ /volatile:iso задает поведение по стандарту, то есть квалификатор volatile отключает оптимизации и к потокобезопасности явного отношения не имеет.
При этом по умолчанию, в MSVC подразумевается /volatile:ms, то есть поведение MSVC в отношении volatile не является стандартным по умолчанию!
Сборка персонального компьютера от Artline: умный выбор для современных пользователей