C++ shared_ptr的线程安全问题
shared_ptr的线程安全问题
首先看看下面的图,这个图描述的就是对象、资源、引用计数之间的关系。
shared_ptr的线程安全问题需要从两个角度来分析:
(1)从引用计数的角度来看:
虽然引用计数存在于每一个shared_ptr
对象中,但是实际上它是要跟随对象所管理的资源。引用计数会随着指向这块资源的shared_ptr
对象的增加而增加。因此引用计数是要指向同一块资源的所有的对象共享的,所以实际上引用计数在shared_ptr
底层中是以指针的形式实现的,所有的对象通过指针访问同一块空间,从而实现共享。
那么也就是说,引用计数是一个临界资源,所以在多线程中,我们必须要保证临界资源访问的安全性,因此在shared_ptr
底层中在对引用计数进行访问之前,首先对其加锁,当访问完毕之后,在对其进行解锁。
所以shared_ptr的引用计数是线程安全的。
(2)从被shared_ptr对象所管理的资源来看:shared_ptr
对象所管理的资源存放在堆上,它可以由多个shared_ptr
所访问,所以这也是一个临界资源。因此当多个线程访问它时,会出现线程安全的问题。
首先shared_ptr
对象有两个变量,一个是指向的对象的指针,还有一个就是我们上面看到的引用计数, 当shared_ptr
发生拷贝的时候,是先拷贝智能指针,然后再拷贝引用计数,也就是说,shared_ptr的拷贝并不是一个原子操作。而问题就出现在这里。
用一个简单的例子来说明:
假如有下面三个同类型的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是一个悬空指针
(4)接下来轮到线程A执行,但是此时线程A是从上一次执行到的地方开始执行,也就是说,线程A会从步骤二开始执行。
所以多个shared_ptr
对象对其所管理的资源的访问不是线程安全的。如果不使用锁这会造成线程安全问题。
(5)所以当我们多个线程访问同一个shared_ptr
时,应该要进行加锁操作。
上一篇: 唐朝宦官仇士良为何能将皇帝玩弄于股掌之间
下一篇: swift 开源框架