shared_ptr实现和线程安全分析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shared_ptr实现和线程安全分析相关的知识,希望对你有一定的参考价值。
参考技术A使用该智能指针(或者其他两种)需要导入头文件 #include <memory>
除此之外还可以对 shared_ptr 赋值,通过重写 operator= 实现。 需要注意 ,对于 p1=p2 (均为智能指针)这种, p2 所指对象由于被 p1 指向,所以该引用计数会加一, p1 原本指向的资源的引用计数会减一。这也会引出下面关于 shared_ptr 指针的 线程安全 问题。
运行结果如下:
这里实现的还有一些问题,因为 shared_ptr 源码中关于引用计数是 原子操作 ,不需要考虑资源使用冲突的问题,可以在自己实现的时候加锁。
首先什么是线程安全?
简单来说就是 多个线程操作一个共享数据,都能按照预期的行为进行,无论多个线程的运行次序如何交织。
对于 shared_ptr ,其内部有两个变量,引用计数和真正的对象类型指针。其中引用计数是原子操作,所以 对于 shared_ptr 的读操作是线程安全的。
但是对于 shared_ptr 中赋值如 ptr1 = ptr2 ,需要两个步骤, 1、 ptr1 的内部对象指针 Obj1 替换成 ptr2 内部对象 Obj2 指针;2、 ptr1 的对于 Obj1 的引用计数缓存 Obj2 的引用计数。
这两步并不是原子的,如果一个线程需要对 shared_ptr 进行赋值操作 ptr1 = ptr2 ,刚完成第一步,就切换到其他线程又对ptr2进行操作,如 ptr2 = ptr3 ,就有可能造成析构了引用计数。而继续之前线程的第二步,就会出错。
总之:对于 shared_ptr 的读操作是线程安全的。
对于 shared_ptr 读写操作不是线程安全的,需要加锁。
tips:为什么 shared_ptr 的引用计数能够同步到不同的指针中?
有人回答可能使用的是static变量,这是不可能的,因为一个类中只有一个静态变量,只能记录对于一个对象的引用次数,这在包含两个 shared_ptr 以上的程序中是不可行的。
个人认为是引用计数是用指针实现的,指向一个记录引用次数的对象。
网易面试题如何实现一个线程安全的shared_ptr智能指针
题目描述
网易一面遇到过这么一个题目,面试官要求自己实现一个线程安全的shared_ptr智能指针。
题目分析
可能很多人只知道shared_ptr是C++11模块库的头文件定义的一个智能指针,即shared_ptr模版。只要将new运算符返回的指针交给shared_ptr这个“管家”,就不必担心在哪里没有delete p了。shared_ptr在被删除的时候会自动执行delete p。通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针,写法如下:
shared_ptr<T> ptr(new T)
此后ptr就像T指针那样用,即ptr就是new动态分配的对象。多个shared_ptr可以共同托管一个指针p,当所有曾经托管p的shared_ptr都解除了对p的托管就会执行delete p。
这个题目拿到手后得搞清楚shared_ptr的原理
1.在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
2.在对象被销毁时,就说明自己不使用该资源了,对象的引用计数减一。
3.如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
4.如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
而且题目既然要求线程安全,就得考虑下面两个问题:
1.智能指针对象中的引用技术是多个对象所共享的,两个线程同时对智能指针进行++或–运算,这个计数原来是1,++了两次可能还是2,这样引用技术就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。
2.智能指针的对象存放在堆上,可能会导致线程安全问题。
线程安全的智能指针代码如下:
#include <utility>
#include <string>
#include <mutex>
using namespace std;
class Counter
public:
Counter() :
m_Counter(0)
Counter(const Counter&) = delete;
Counter& operator=(const Counter&) = delete;
~Counter()
void reset()
m_Counter = 0;
unsigned int get() const
return m_Counter;
void operator++()
m_Counter++;
void operator++(int)
m_Counter++;
void operator--()
m_Counter--;
void operator--(int)
m_Counter--;
friend ostream& operator<<(ostream& os, const Counter& c)
os << "Counter value: " << c.m_Counter << endl;
return os;
private:
unsigned int m_Counter;
;
template<typename T>
class Shared_ptr
public:
explicit Shared_ptr(T *ptr=nullptr):
m_ptr(ptr),
m_pCounter(new Counter()),
m_pMutex(new std::mutex)
if(ptr)
AddRefCount();
Shared_ptr(Shared_ptr<T>& sp)
m_ptr = sp.m_ptr;
m_pCounter = sp.m_pCounter;
AddRefCount();
Shared_ptr<T>& operator=(const Shared_ptr<T>& sp)
if(m_ptr != sp.m_ptr)
Realse();
m_ptr = sp.m_ptr;
m_pCounter = sp.m_pCounter;
m_pMutex = sp.m_pMutex;
AddRefCount();
T *operator->()
return m_ptr;
T& operator*()
return *m_ptr;
T *get()
return m_ptr;
unsigned int UseCount()
return m_pCounter->get();
~Shared_ptr()
Realse();
private:
void AddRefCount()
m_pMutex->lock();
++(*m_pCounter);
m_pMutex->unlock();
void Realse()
bool deleteflag = false;
m_pMutex->lock();
--(*m_pCounter);
if(m_pCounter->get() == 0)
delete m_pCounter;
delete m_ptr;
deleteflag = true;
m_pMutex->unlock();
if(deleteflag == true)
delete m_pMutex;
private:
T *m_ptr;
std::mutex *m_pMutex;
Counter *m_pCounter;
;
以上是关于shared_ptr实现和线程安全分析的主要内容,如果未能解决你的问题,请参考以下文章
网易面试题如何实现一个线程安全的shared_ptr智能指针
网易面试题如何实现一个线程安全的shared_ptr智能指针