С++ 11: Безопасная двойная проверка блокировки для ленивой инициализации. Возможное?

Я прочитал много вопросов, связанных с безопасным двойным проверением блокировки (для одиночных или ленивых init). В некоторых потоках ответ заключается в том, что шаблон полностью сломан, другие предлагают решение.

Итак, мой вопрос: есть ли способ написать полностью проверенный по потоку двойной проверенный шаблон блокировки в С++? Если да, то как это выглядит.

Мы можем предположить С++ 11, если это упростит ситуацию. Насколько я знаю, С++ 11 улучшил модель памяти, которая может обеспечить необходимые улучшения.

Я знаю, что это возможно на Java, делая двойную проверку защищенной переменной volatile. Поскольку С++ 11 заимствовал большие части модели памяти из одной из Java, поэтому я думаю, что это возможно, но как?

Ответ 1

Просто используйте статическую локальную переменную для лениво инициализированных синглтонов, например:

MySingleton* GetInstance() {
  static MySingleton instance;
  return &instance; 
}

Стандарт (С++ 11) уже гарантирует, что статические переменные инициализируются в потокобезопасном режиме, и представляется вероятным, что реализация этого, по крайней мере, столь же надежная и эффективная, как и все, что вы сами напишете.

Понятия о потоках инициализации можно найти в п. 6.7.4 стандарта (С++ 11):

Если элемент управления входит в объявление одновременно при инициализации переменной, одновременное выполнение должно ждать завершения инициализации.

Ответ 2

Поскольку вы хотели увидеть действительную реализацию DCLP С++ 11, вот один из них.

Поведение полностью потокобезопасно и идентично GetInstance() в ответе Grizzly.

std::mutex mtx;
std::atomic<MySingleton *> instance_p{nullptr};

MySingleton* GetInstance()
{
    auto *p = instance_p.load(std::memory_order_acquire);

    if (!p)
    {
        std::lock_guard<std::mutex> lck{mtx};

        p = instance_p.load(std::memory_order_relaxed);
        if (!p)
        {
            p = new MySingleton;
            instance_p.store(p, std::memory_order_release);
        }
    }

    return p;
}