Я прочитал много вопросов, касающихся поточно-ориентированной блокировки с двойной проверкой (для одиночных или ленивых инициалов). В некоторых потоках ответ таков: шаблон полностью нарушен, другие предлагают решение.
Итак, мой вопрос: есть ли способ написать полностью поточно-ориентированный шаблон блокировки с двойной проверкой в C ++? Если так, то как это выглядит.
Мы можем принять C ++ 11, если это облегчает ситуацию. Насколько я знаю, C ++ 11 улучшил модель памяти, которая могла дать необходимые улучшения.
Я знаю, что это возможно в Java, сделав двойную проверку защищенной переменной volatile. Поскольку C ++ 11 позаимствовал большую часть модели памяти из модели Java, я думаю, что это возможно, но как?
Просто используйте статическую локальную переменную для лениво инициализированных синглетонов, вот так:
MySingleton* GetInstance() {
static MySingleton instance;
return &instance;
}
Стандарт (C ++ 11) уже гарантирует, что статические переменные инициализируются потокобезопасным способом, и кажется вероятным, что реализация этого по крайней мере столь же надежна и эффективна, как и все, что вы пишете сами.
Потоковая безопасность инициализации может быть найдена в §6.7.4 стандарта (C ++ 11):
Если во время инициализации переменной элемент управления вводит объявление одновременно, параллельное выполнение должно ожидать завершения инициализации.
Так как вы хотели увидеть правильную реализацию DCLP C ++ 11, вот один из них.
Поведение полностью потокобезопасно и идентично GetInstance()
в ответе Гризли.
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;
}