shared_ptr在多线程下的安全性问题
1. 引用
boost官方文档中有如下结论:
https://www.boost.org/doc/libs/1_57_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety
1)同一个shared_ptr被多个线程“读”是安全的;
2)同一个shared_ptr被多个线程“写”是不安全的;
3)共享引用计数的不同的shared_ptr被多个线程”写“ 是安全的;
- A
shared_ptr
instance can be "read" (accessed using only const operations) simultaneously by multiple threads. - Different
shared_ptr
instances can be "written to" (accessed using mutable operations such asoperator=
orreset
) simultaneously by multiple threads (even when these instances are copies, and share the same reference count underneath.) - Any other simultaneous accesses result in undefined behavior.
2. 线程安全
shared_ptr指针类有两个成员变量,一个是指向变量的指针;一个是资源被引用的次数,引用次数加减操作内部自动加锁解锁,是线程安全的。
2.1 引用计数
虽然引用计数存在于每一个shared_ptr对象中,但是实际上它是要跟随对象所管理的资源。引用计数会随着指向这块资源的shared_ptr对象的增加而增加。因此引用计数是要指向同一块资源的所有的对象共享的,所以实际上引用计数在shared_ptr底层中是以指针的形式实现的,所有的对象通过指针访问同一块空间,从而实现共享。
那么也就是说,引用计数是一个临界资源,所以在多线程中,我们必须要保证临界资源访问的安全性,因此在shared_ptr底层中在对引用计数进行访问之前,首先对其加锁,当访问完毕之后,在对其进行解锁。
所以shared_ptr的引用计数是线程安全的。
2.2 被shared_ptr对象所管理的资源
shared_ptr
对象所管理的资源存放在堆上,它可以由多个shared_ptr
所访问,所以这也是一个临界资源。因此当多个线程访问它时,会出现线程安全的问题。
3. 一个例子
shared_ptr发生拷贝的流程:
1)拷贝智能指针指向的资源(非原子操作)
2)增减引用计数(原子操作)
假如有下面三个同类型的shared_ptr:
shared_ptr<foo> p1; //线程A的局部变量 shared_ptr<foo> p2(new foo); //线程A和线程B所共享 shared_ptr<foo> p3(new foo); //线程B的局部变量
1)一开始他们之间的关系可以用下图来表示:
2)然后线程A先执行语句:p1=p2,在执行这条语句时,先改变ptr的指向,然后才修改引用计数。因为现在是多线程,所以很可能出现这样的情况:在线程A执行完步骤一时,还没来得及执行步骤二,就轮到线程B来执行。如下图所示:
3)现在线程B开始执行p2=p3,并且没有被打断,也就是说步骤一二都完成。
先是步骤一:
然后步骤二:
注意此时因为第一个资源的引用计数已经为0,所以会销毁该资源,也就是说,步骤二执行完之后,p1的ptr是一个悬空指针
所以多个shared_ptr对象对其所管理的资源的访问不是线程安全的。如果不使用锁这会造成线程安全问题。
结论:所以当我们多个线程访问同一个shared_ptr时,应该要进行加锁操作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了