Дважды проверил шаблон блокировки в C++11?
Новая модель машины C++11 позволяет надежно работать многопроцессорным системам. реорганизации инструкций.
Как отметили Мейерс и Александреску, "простая" реализация шаблона блокировки с двойной проверкой небезопасна в C++03
Singleton* Singleton::instance() {
if (pInstance == 0) { // 1st test
Lock lock;
if (pInstance == 0) { // 2nd test
pInstance = new Singleton;
}
}
return pInstance;
}
В своей статье они показали, что независимо от того, что вы делаете как программист, в C++03 у компилятора слишком много свободы: разрешается переупорядочивать инструкции таким образом, что вы не можете быть уверены, что в итоге вы получите только один экземпляр Singleton
,
Мой вопрос сейчас:
- Ограничивают ли ограничения / определения новой машинной модели C++11 последовательность инструкций, чтобы приведенный выше код всегда работал с компилятором C++11?
- Как выглядит безопасная C++11-реализация этого шаблона Singleton при использовании новых библиотечных средств (вместо макета
Lock
Вот)?
3 ответа
Если pInstance
является обычным указателем, код имеет потенциальную гонку данных - операции над указателями (или любым встроенным типом, в этом отношении) не гарантированно являются атомарными (EDIT: или хорошо упорядочены)
Если pInstance
является std::atomic<Singleton*>
а также Lock
внутренне использует std::mutex
добиться синхронизации (например, если Lock
на самом деле std::lock_guard<std::mutex>
), код должен быть свободен от данных.
Обратите внимание, что вам нужна как явная блокировка, так и атомарный pInstance
добиться правильной синхронизации.
Поскольку инициализация статической переменной теперь гарантированно безопасна для потоков, синглтон Мейера должен быть безопасным для потоков.
Singleton* Singleton::instance() {
static Singleton _instance;
return &_instance;
}
Теперь вам нужно решить главную проблему: в вашем коде есть синглтон.
РЕДАКТИРОВАТЬ: на основе моего комментария ниже: Эта реализация имеет большой недостаток по сравнению с другими. Что произойдет, если компилятор не поддерживает эту функцию? Компилятор будет выдавать поток небезопасного кода даже без выдачи предупреждения. Другие решения с блокировками не будут компилироваться, даже если компилятор не поддерживает новые интерфейсы. Это может быть хорошей причиной, чтобы не полагаться на эту функцию, даже для вещей, отличных от одиночных.
C++11 не меняет смысла этой реализации двойной проверки блокировки. Если вы хотите, чтобы блокировка с двойной проверкой работала, вам необходимо установить подходящие барьеры / ограждения памяти.