С++ 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;
}