std::shared_ptr的引用计数操作线程安全,但其管理的对象访问、自身变量赋值及裸指针传递均不安全,须分别加锁或使用atomic/weak_ptr等机制防护。

std::shared_ptr 的引用计数操作线程安全,但它管理的对象、它自身变量的赋值、以及裸指针完全不安全——三者必须分开处理,否则必出问题。
std::shared_ptr 的引用计数是原子的,但对象访问不是
多个线程同时拷贝或析构同一个 std::shared_ptr 实例(比如传值给 lambda 或函数参数),引用计数增减由原子 CAS 保证,不会损坏。但一旦你通过 operator-> 或 operator* 访问其指向的对象,就进入普通数据竞争区。
- 错误写法:
sp->value++在多个线程中无锁调用 → 非原子读-改-写,结果不可预测 - 正确做法:对
*sp的读写必须加锁(如std::mutex)或使用std::atomic<T>封装内部字段 - 注意:
std::shared_ptr<std::atomic<int>>是合法的,但只保护int本身,不保护shared_ptr变量的赋值
对同一个 std::shared_ptr 变量并发赋值必须加锁
如果多个线程反复执行 ptr = std::make_shared<X>() 或 ptr.reset(),ptr 本身是普通对象,其内部指针和控制块指针的更新非原子。C++20 前没有内置方案,必须同步。
- 典型崩溃场景:线程 A 正在
reset(),线程 B 同时operator=→ptr内部状态错乱,后续lock()或解引用可能 crash - 解决方式:用
std::mutex包裹所有对同一std::shared_ptr变量的写操作 - C++20 起可用
std::atomic<std::shared_ptr<T>>替代,但注意它只支持load()/store(),不支持reset()等复杂操作
weak_ptr::lock() 是线程安全的,但返回值必须判空
weak_ptr::lock() 是多线程中唯一能安全“尝试获取强引用”的接口,它内部对控制块的读取和引用计数增加是原子的。但它不承诺成功,也不阻塞。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 常见错误:直接
*wp.lock()→ 若对象已销毁,lock()返回空shared_ptr,解引用导致 crash - 正确模式:
if (auto sp = wp.lock()) { use(*sp); },且use()中若需长时间持有对象,应确保sp生命周期覆盖整个使用过程 - 注意:
expired()仅做快照判断,之后对象仍可能被销毁;lock()才真正延长生命周期,更可靠
裸指针跨线程传递等于埋雷
把 int*、MyClass* 这类裸指针传给新线程,等同于放弃所有权和生命周期管理。即使原 shared_ptr 还活着,也无法阻止它提前释放。
- 危险示例:
std::thread t([](int* p) { std::this_thread::sleep_for(1s); printf("%d", *p); }, ptr.get());→ptr可能在t执行前就析构 - 安全替代:传
std::shared_ptr<T>值(触发引用计数+1),或用std::weak_ptr<T>+lock()动态检查 - 特别提醒:
std::unique_ptr跨线程必须用std::move()转移,且只能转移一次;不能复制,也不能用get()提取裸指针再传
最容易被忽略的一点:智能指针的“线程安全”永远只针对控制块,从不延伸到所指对象的数据布局。哪怕你用了十个 shared_ptr 指向同一个 std::vector,只要没加锁或没用 std::atomic,push_back 依然会崩。

















