shared_ptr的引用计数操作是线程安全的,但其指向对象的访问、同一shared_ptr变量的多线程修改及自定义删除器执行均需额外同步。

shared_ptr 的引用计数操作是线程安全的
多个线程同时拷贝、赋值或析构不同的 shared_ptr 副本(指向同一对象)时,引用计数的增减是原子的,不会出现计数错乱或提前释放。这是 C++ 标准明确保证的:shared_ptr 控制块中的 shared_count 和 weak_count 都是 std::atomic<size_t></size_t> 类型。
典型安全场景包括:
- 全局
shared_ptr被多个线程按值捕获([local_ptr = global_ptr]),各自持有独立副本 - 一个线程在读取对象,另一个线程刚好销毁其持有的
shared_ptr—— 只要还有其他副本,对象就不会被释放 - 用
weak_ptr::lock()在多线程中安全尝试获取有效shared_ptr,失败返回空指针,无竞态
同一个 shared_ptr 对象被多线程读写是不安全的
如果多个线程通过引用或指针访问**同一个** shared_ptr 变量(比如全局变量、类成员、lambda 按引用捕获的变量),并执行 reset()、赋值(=)、移动(std::move())等修改操作,就会引发数据竞争。
错误示例的关键问题在于:
-
sp.reset(new int(i))会同时修改sp内部的两个指针(对象指针 + 控制块指针),不是原子操作 - 不同线程可能覆盖彼此的写入,导致控制块泄漏、对象重复析构或访问已释放内存
- C++20 之前没有内置机制保护这种操作,必须加
std::mutex
C++20 起可改用 std::atomic<:shared_ptr>></:shared_ptr> 替代裸 shared_ptr 变量,但注意它仅对指针本身原子,不保护所指对象。
shared_ptr 指向的对象访问不是自动线程安全的
shared_ptr 只管生命周期,不管对象内部状态。即使引用计数安全,多个线程通过各自副本调用 (*ptr)->method() 或修改成员变量,仍会产生数据竞争。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
常见误判点:
- 看到
shared_ptr多线程“能用”,就默认Foo类的baz成员可并发读写 - 把
shared_ptr当作“线程安全容器”,忽略对象本身的同步需求 - 未意识到
make_shared分配的内存和控制块是连续的,但对象内容仍需独立保护
正确做法是:对共享对象的读写操作,必须由外部同步机制(如 std::mutex、std::atomic 成员)保障,不能依赖 shared_ptr。
循环引用和 weak_ptr 不解决线程安全问题
weak_ptr 主要用于打破循环引用,避免内存泄漏;它的 lock() 是线程安全的,但返回的 shared_ptr 仍需按前述规则使用。
容易被忽略的细节:
-
weak_ptr本身不增加引用计数,但lock()成功后得到的shared_ptr副本才参与计数 —— 这个过程是安全的,但后续对该对象的操作仍需同步 - 用
weak_ptr替代shared_ptr并不能绕过对象访问的竞态,只是让“是否还活着”的判断更安全 - 控制块的原子性只覆盖引用计数,不覆盖自定义删除器或分配器的执行逻辑 —— 若删除器有副作用,仍需确保线程安全
真正麻烦的从来不是“怎么让 shared_ptr 不崩溃”,而是“怎么让人忘记它只管计数,不管数据”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










