TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #300 3.72K
Double-Checked Locking Pattern. Мотивация
#новичкам

Михаил на ретро предложил идею посмотреть в прошлое на определенную проблему и понять, как изменялись подходы к решению проблемы. Тут не прям сильно далеко пойдем и сильно много итераций будем рассматривать, но все же. Также были запросы на многопоточку и паттерны плюсовые. Собсна, все это комбинируя с большой темой статиков, начинаем изучать паттерн Блокировки с двойной проверкой.

Начнем с того, что в стародавние времена до С++11 у нас была довольно примитивная модель памяти, которая вообще не знала о существовании потоков. И не было вот этой гарантии для статических локальных переменных:

...
If control enters the declaration concurrently
while the variable is being initialized,
the concurrent execution shall wait for
completion of the initialization.


Поэтому раньше люди не могли писать такой простой код:

Singleton& GetInstance() {
static Singleton inst{};
return inst;
}


и надеяться на то, что объект будет создаваться потокобезопасно.

Самое простое, что можно здесь придумать - влепить замок и не париться.

class Singleton {
public:
static Singleton* instance() {
Lock lock;
if (inst_ptr == NULL) {
inst_ptr = new Singleton;
}
return inst_ptr;
}
private:
Singleton() = default;
static Singleton* inst_ptr;
};


У нас есть какая-то своя RAII обертка Lock над каким-то мьютексом(до С++11 ни std::mutex, ни std::lock_guard не существовало, приходилось велосипедить(ну или бустовать, кому как удобнее)).

Обратите внимание, как это работает. Статические указатели автоматически zero-инициализируются нулем, поэтому в начале inst_ptr равен NULL. Дальше нужно проверить, если указатель еще нулевой, то значит мы ничего не проинициализировали и нужно создать объект. И делать это должен один тред. Но куда поставить лок? На весь скоуп или только внутри условия на создание объекта?

Дело в том, что может получиться так, что несколько потоков одновременно войдут в условие. Но только один из них успешно возьмет лок. Создаст объект и отпустит мьютекс. Но другие потоки-то уже вошли в условие. И когда настанет их черед выполняться, то они просто будут пересоздавать объекты и мы получим бог знает какие сайдэффекты. Плюс утечку памяти, так как изначально созданные объект потеряется навсегда. Плюс получается, что наш синглтон не такой уж и сингл...

Именно поэтому замок должен стоять с самого начала, чтобы только один поток вошел в условие. И создал объект. А все остальные потоки будут просто пользоваться этим объектом, обходя условие.

Однако теперь возникает проблема. Нам, вообще говоря, этот замок нахрен не сдался после того, как мы создали объект. Захват и освобождение мьютекса - довольно затратные операции и не хотелось бы их каждый раз выполнять, когда мы просто хотим получить доступ к нашему объекту-одиночке. И было бы очень удобно перенести этот лок в условие. Но в текущей реализации это невозможно...

Здесь-то и приходит на помощь шаблон блокировки с двойной проверкой, о котором подробнее поговорим в следующих статьях.

Solve your problems. Stay cool.

#concurrency #cppcore #cpp11
  • 👍 23
  • 🔥 5
  • ❤ 3
  • ⚡ 2
  • ❤‍🔥 1
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →