# Потокобезопасен ли `std::shared_ptr`? Пожалуй, это самый популярный вопрос для собеседования на позицию C++-разработчика. И не без причины: этим прекрасным умным указателем так просто пользоваться (в сравнении с его собратом — `std::unique_ptr`), что легко не заметить подвох. В его названии есть `shared`. Да он и спроектирован так, чтобы его можно было разделять между потоками. Что может пойти не так?! Всё. Новички довольно быстро обнаруживают первую линию костыльно-грабельной обороны бастиона сложности `shared_ptr`: если доступ к самому указателю `shared_ptr` «безопасен», то к объекту `T` все равно надо синхронизировать. Это очевидно, это заметно, это понятно. Но дальше ведь все просто? Нет. Дальше притаились волчьи ямы с отравленными копьями. Сам объект-указатель `shared_ptr` не является потокобезопасным. И доступ к самому указателю тоже надо синхронизировать! Как же так?! Мы никогда не синхронизировали и у нас все работало. Поздравляю, у вас одно из двух: 1. Либо все доступы к указателю из разных потоков только на чтение. И тогда проблем действительно нет. 2. Программа работает по воле случая. ```C++ using namespace std::literals::chrono_literals; std::shared_ptr str = nullptr; std::jthread t1 { [&]{ std::size_t cnt_miss = 0; while (!str) { ++cnt_miss; } std::cout << "count miss: " << cnt_miss << "\n"; std::cout << *str << "\n"; } }; std::jthread t2 { [&] { std::this_thread::sleep_for(500ms); str = std::make_shared("Hello World"); } }; ``` Аналогично другим примерам с [race condition](./race_condition.md) код выше [перестает](https://godbolt.org/z/zocsYo) работать при изменении уровня оптимизации. Но ведь вы наверняка что-то слышали; все-таки есть в `shared_ptr` кое-что потокобезопасное... Да. Есть. Счетчик ссылок. Больше ничего потокобезопасного в `std::shared_ptr` нет. Атомарный счетчик ссылок как раз и позволяет без проблем копировать один и тот же указатель (увеличивая счетчики) в разные потоки и не синхронизировать вручную вызовы деструкторов (уменьшающих счетчики) в разных потоках. Если вам надо менять указатель из разных потоков, то вам нужен `std::atomic>` (C++20). Либо использовать функции ` std::atomic_load`/`std::atomic_store` и прочие — у них есть специальные перегрузки для `shared_ptr`. С `std::weak_ptr` все то же самое. ## Полезные ссылки 1. https://stackoverflow.com/questions/9127816/stdshared-ptr-thread-safety-explained 2. https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2