欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++ shared_ptr的线程安全问题

程序员文章站 2022-06-10 22:17:21
...

shared_ptr的线程安全问题

首先看看下面的图,这个图描述的就是对象、资源、引用计数之间的关系。
C++ 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)一开始他们之间的关系可以用下图来表示:
C++ shared_ptr的线程安全问题
(2)然后线程A先执行语句:p1=p2,在执行这条语句时,先改变ptr的指向,然后才修改引用计数。因为现在是多线程,所以很可能出现这样的情况:在线程A执行完步骤一时,还没来得及执行步骤二,就轮到线程B来执行。如下图所示:
C++ shared_ptr的线程安全问题
(3)现在线程B开始执行p2=p3,并且没有被打断,也就是说步骤一二都完成。
先是步骤一
C++ shared_ptr的线程安全问题
然后步骤二:
C++ shared_ptr的线程安全问题
注意此时因为第一个资源的引用计数已经为0,所以会销毁该资源,也就是说,步骤二执行完之后,p1的ptr是一个悬空指针

(4)接下来轮到线程A执行,但是此时线程A是从上一次执行到的地方开始执行,也就是说,线程A会从步骤二开始执行。
C++ shared_ptr的线程安全问题
所以多个shared_ptr对象对其所管理的资源的访问不是线程安全的。如果不使用锁这会造成线程安全问题。

(5)所以当我们多个线程访问同一个shared_ptr时,应该要进行加锁操作。

相关标签: C++ c++