c++oding="utf-8" ?>
std::shared_ptr的引用计数是原子操作,线程安全;但被管理对象的访问、shared_ptr实例本身的读写(如reset/赋值)及自定义计数器均不自动线程安全,需额外同步。

std::shared_ptr 的引用计数不是原子操作?
不是。std::shared_ptr 的引用计数本身是线程安全的——它的控制块(control block)中对引用计数的增减(++ 和 --)由标准库保证为原子操作,前提是使用的是标准实现(如 libstdc++、libc++ 或 MSVC STL)。但「线程安全」仅针对引用计数本身,不等于「所有涉及 shared_ptr 的代码都线程安全」。
真正出问题的地方:共享对象的非原子访问
常见错误不是引用计数错了,而是你误把「引用计数」当成「业务逻辑计数」来用。比如:
- 你写
sp->count_++(count_是sp所指对象的成员变量),这不是引用计数,这是普通整型变量,没加锁就多线程读写必然错 - 你用
std::shared_ptr<person></person>管理对象,但多个线程同时调用person->doWork(),而doWork()内部修改了成员变量,且没同步 - 你把
std::shared_ptr存在全局变量或静态容器里,然后多线程直接赋值/拷贝/重置,虽然引用计数安全,但容器本身(如std::vector<:shared_ptr>></:shared_ptr>)不提供并发访问保护
std::shared_ptr 拷贝/析构时的竞态条件场景
引用计数原子性只保「单次」operator=、copy constructor、reset() 的计数变更安全。但如果这些操作和对象析构交叉发生,可能触发未定义行为:
- 线程 A 正在执行
ptr.reset()(引用计数从 1→0,触发delete) - 线程 B 同时执行
auto p = ptr;(引用计数从 0→1?不,此时控制块可能已被销毁) - 结果:B 得到一个指向已释放内存的
std::shared_ptr,后续解引用崩溃 - 这不是计数“算错”,而是「访问已销毁控制块」——根本原因是没有保证
shared_ptr实例本身的并发访问安全
解决办法:对共享的 std::shared_ptr 变量(尤其是全局/静态/类成员)加锁,或改用 std::atomic<:shared_ptr>></:shared_ptr>(C++20 起支持)。
为什么智能指针源码里 count++ 还会出错?
如果你看到类似「count++ 在多线程下结果小于预期」,那基本可以确定你不是在操作 std::shared_ptr 的引用计数,而是在操作自己写的计数器——比如智能指针内部封装的 strong_count_ 成员(如 Android 的 sp<t></t>),它默认不带原子性。这类自研智能指针若没用 std::atomic_int 或 __atomic_add_fetch 等做原子操作,就会出现读-改-写竞态。
典型表现:
- 两个线程同时执行
strong_count_++ - 都读到值为 1 → 都算出 2 → 都写回 2 → 最终结果是 2,而非预期的 3
- 这种问题在
sp<t></t>、wp<t></t>(Android)、或手写 RC 智能指针中高频出现
修复方式:把 int strong_count_; 改成 std::atomic_int strong_count_{0};,并用 fetch_add(1) 替代 ++。
真正容易被忽略的点:引用计数线程安全 ≠ 对象线程安全 ≠ 智能指针变量线程安全。三者必须分开看待,各自加对应保护。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











