Ответ
Выбор зависит от модели владения данными:
-
std::unique_ptr<T>— для эксклюзивного владения, когда ресурс принадлежит только одному потоку в каждый момент времени. Сам по себе он не потокобезопасен для передачи владения между потоками без синхронизации. Однако его можно безопасно перемещать (std::move) в другой поток, если гарантируется, что старый поток больше к нему не обращается. -
std::shared_ptr<T>— для разделяемого владения несколькими потоками. Критически важно понимать: потокобезопасны только контрольный блок (счётчик ссылок)shared_ptr, но не данные, на которые он указывает. Несколько потоков могут безопасно создавать/уничтожать свои копииshared_ptr, указывающие на один объект.
// ПРАВИЛЬНО: Безопасное копирование shared_ptr между потоками
void reader(const std::shared_ptr<const Config>& config) {
// Чтение config безопасно, если объект Config неизменяем (const)
}
// НЕПРАВИЛЬНО: Без синхронизации запись в общий объект через shared_ptr — data race
void unsafe_writer(std::shared_ptr<Config>& config) {
config->value = 42; // ПОТЕНЦИАЛЬНЫЙ DATA RACE!
}
// ПРАВИЛЬНО: Защита данных мьютексом
struct ThreadSafeConfig {
std::shared_ptr<Config> data;
std::mutex mtx;
};
std::atomic<std::shared_ptr<T>>(C++20) — предоставляет атомарные операции для замены одногоshared_ptrна другой (например,load,store,exchange). Это полезно для реализаций типа lock-free RCU (Read-Copy-Update).
Итог: Сам по себе умный указатель не синхронизирует доступ к данным. Для изменяемых разделяемых данных всегда нужен дополнительный механизм синхронизации (мьютекс, атомарные переменные).