Дважды проверил шаблон блокировки в 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 не меняет смысла этой реализации двойной проверки блокировки. Если вы хотите, чтобы блокировка с двойной проверкой работала, вам необходимо установить подходящие барьеры / ограждения памяти.

Другие вопросы по тегам