← Назад к вопросам

Потокобезопасен ли shared_ptr?

2.3 Middle🔥 191 комментариев
#Умные указатели и управление памятью#Многопоточность и синхронизация

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Потокобезопасность shared_ptr

Этот вопрос часто вызывает путаницу, потому что ответ подразумевает уточнение: shared_ptr потокобезопасен частично.

Что потокобезопасно

Операции над счётчиком ссылок — атомарны:

std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
std::shared_ptr<int> ptr2;

// Поток 1
ptr2 = ptr1; // атомарная операция захвата счётчика

// Поток 2
std::shared_ptr<int> ptr3 = ptr1; // тоже безопасно

Потокобезопасны:

  • Копирование shared_ptr
  • Перемещение (move)
  • Захват и отпуск счётчика ссылок
  • get(), use_count()

Что НЕ потокобезопасно

Доступ к управляемому объекту:

std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();

// Поток 1
ptr->method(); // NOT THREAD-SAFE

// Поток 2
ptr->method(); // Может произойти race condition на данных MyClass

Присваивание нескольким потокам:

std::shared_ptr<int> shared;

// Поток 1
shared = std::make_shared<int>(1); // Читает и пишет указатель

// Поток 2
shared = std::make_shared<int>(2); // Race condition!

Правильный порядок синхронизации

std::shared_ptr<MyClass> shared;
std::mutex mtx;

void update() {
    auto new_obj = std::make_shared<MyClass>();
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        shared = new_obj; // Присваивание под блокировкой
    }
}

void use() {
    std::shared_ptr<MyClass> local;
    {
        std::lock_guard<std::mutex> lock(mtx);
        local = shared; // Копируем под блокировкой
    }
    
    // Теперь можем работать с local без блокировки
    if (local) {
        local->method();
    }
}

Почему это важно

Счётчик ссылок — внутреннее состояние shared_ptr, его целостность гарантирована атомарными операциями. Но:

  1. Структура shared_ptr содержит два указателя (на контрольный блок и объект)
  2. Присваивание shared_ptr — не атомарная операция на уровне самого shared_ptr
  3. Гарантия распространяется только на счётчик, не на сам указатель

Вывод

  • shared_ptr безопасен для читателей одного указателя с разными потоками
  • shared_ptr БЕЗ СИНХРОНИЗАЦИИ — если изменяются присваивания переменной
  • Объект, на который указывает shared_ptr — требует отдельной синхронизации
  • Используй мьютекс вокруг присваиваний и чтения shared_ptr из разных потоков