文章目录
shared_ptr模拟实现
- 代码实现
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
template<class T>
class Shared_Ptr{
public:
Shared_Ptr(T* ptr=nullptr)
:_ptr(ptr)
,_UseCount(new int(1))
,_mut(new mutex)
{
}
~Shared_Ptr(){
Release();
}
Shared_Ptr(const Shared_Ptr<T>& sp)
:_ptr(sp._ptr)
,_UseCount(sp._UseCount)
,_mut(sp._mut)
{
Add_UseCount();
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp){
if(_ptr!=sp._ptr){
//释放管理的旧的资源
Release();
//共享管理对象的资源,并增加引用计数
_ptr=sp._ptr;
_UseCount=sp._UseCount;
_mut=sp._mut;
Add_UseCount();
}
return *this;
}
T& operator*(){
return *_ptr;
}
T* operator->(){
return _ptr;
}
void Add_UseCount(){
//加锁或者使用加1的原子操作
_mut->lock();
++(*_UseCount);
_mut->unlock();
}
int UseCount(){
return *_UseCount;
}
private:
void Release(){
//用于控制引用计数
bool deleteFalg=false;
_mut->lock();
if(--(*_UseCount)==0){
delete _ptr;
delete _UseCount;
deleteFalg=true;
}
_mut->unlock();
if(deleteFalg==true){
delete _mut;
}
}
private:
T* _ptr;
int* _UseCount;
mutex* _mut;
};
int main(){
Shared_Ptr<int> sp(new int(10));
Shared_Ptr<int> sp1(sp);
*sp1=20;
cout<<"sp.UseCount():"<<sp.UseCount()<<endl;
cout<<"sp1.UseCount():"<<sp1.UseCount()<<endl;
Shared_Ptr<int> sp2(new int(10));
sp1=sp2;
cout<<"sp.UseCount():"<<sp.UseCount()<<endl;
cout<<"sp1.UseCount():"<<sp1.UseCount()<<endl;
cout<<"sp2.UseCount():"<<sp2.UseCount()<<endl;
sp=sp2;
cout<<"sp.UseCount():"<<sp.UseCount()<<endl;
cout<<"sp1.UseCount():"<<sp1.UseCount()<<endl;
cout<<"sp2.UseCount():"<<sp2.UseCount()<<endl;
return 0;
}
- 运行结果
std::shared_ptr 的线程安全问题
- std::shared_ptr的线程安全问题
- 通过下面的程序我们来测试shared_ptr的线程安全问题。需要注意的是shared_ptr的线程安全分为两方面:
- 1.智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2.这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、–是需要加锁的,也就是说引用计数的操作是线程安全的。
- 2.智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。
线程安全版本的shared_ptr使用
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex mux;
struct Date{
int _year=1;
int _month=1;
int _day=1;
~Date(){
cout<<"~Date()"<<endl;
}
};
template<class T>
class Shared_Ptr{
private:
T* _Ptr;
int *_UseCount;
mutex *_Mux;
public:
Shared_Ptr(T* ptr)
:_Ptr(ptr)
,_UseCount(new int(1))
,_Mux(new mutex)
{
}
Shared_Ptr(const Shared_Ptr<T>& sp)
:_Ptr(sp._Ptr)
,_UseCount(sp._UseCount)
,_Mux(sp._Mux)
{
addRef();
}
int addRef(){
_Mux->lock();
++(*_UseCount);
_Mux->unlock();
return *_UseCount;
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp){
if(_Ptr!=sp._Ptr){
if(subRef()==0){
delete _Ptr;
delete _UseCount;
delete _Mux;
_Ptr=nullptr;
_UseCount=nullptr;
_Mux=nullptr;
}
_Ptr=sp._Ptr;
_UseCount=sp._UseCount;
_Mux=sp._Mux;
}
return *this;
}
int subRef(){
_Mux->lock();
--(*_UseCount);
_Mux->unlock();
return *_UseCount;
}
~Shared_Ptr(){
if(subRef()==0){
delete _Ptr;
delete _UseCount;
delete _Mux;
_Mux=nullptr;
_UseCount=nullptr;
_Ptr=nullptr;
}
}
T& operator*(){
return *_Ptr;
}
T* operator->(){
return _Ptr;
}
int UseCount(){
return *_UseCount;
}
};
Shared_Ptr<Date> sp(new Date);
void Test(){
for(size_t i=0;i<1000;i++){
Shared_Ptr<Date> p(sp);
mux.lock();
p->_year++;
p->_month++;
p->_day++;
mux.unlock();
}
}
int main(){
thread t1(Test);
thread t2(Test);
t1.join();
t2.join();
cout<<sp->_year<<endl;
cout<<sp->_month<<endl;
cout<<sp->_day<<endl;
return 0;
}
- 运行结果
线程不安全版本
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex mux;
struct Date{
int _year=1;
int _month=1;
int _day=1;
~Date(){
cout<<"~Date()"<<endl;
}
};
template<class T>
class Shared_Ptr{
private:
T* _Ptr;
int *_UseCount;
mutex *_Mux;
public:
Shared_Ptr(T* ptr)
:_Ptr(ptr)
,_UseCount(new int(1))
,_Mux(new mutex)
{
}
Shared_Ptr(const Shared_Ptr<T>& sp)
:_Ptr(sp._Ptr)
,_UseCount(sp._UseCount)
,_Mux(sp._Mux)
{
addRef();
}
int addRef(){
_Mux->lock();
++(*_UseCount);
_Mux->unlock();
return *_UseCount;
}
Shared_Ptr<T>& operator=(const Shared_Ptr<T>& sp){
if(_Ptr!=sp._Ptr){
if(subRef()==0){
delete _Ptr;
delete _UseCount;
delete _Mux;
_Ptr=nullptr;
_UseCount=nullptr;
_Mux=nullptr;
}
_Ptr=sp._Ptr;
_UseCount=sp._UseCount;
_Mux=sp._Mux;
}
return *this;
}
int subRef(){
_Mux->lock();
--(*_UseCount);
_Mux->unlock();
return *_UseCount;
}
~Shared_Ptr(){
if(subRef()==0){
delete _Ptr;
delete _UseCount;
delete _Mux;
_Mux=nullptr;
_UseCount=nullptr;
_Ptr=nullptr;
}
}
T& operator*(){
return *_Ptr;
}
T* operator->(){
return _Ptr;
}
int UseCount(){
return *_UseCount;
}
};
Shared_Ptr<Date> sp(new Date);
void Test(){
for(size_t i=0;i<1000;i++){
Shared_Ptr<Date> p(sp);
//mux.lock();
p->_year++;
p->_month++;
p->_day++;
//mux.unlock();
}
}
int main(){
thread t1(Test);
thread t2(Test);
t1.join();
t2.join();
cout<<sp->_year<<endl;
cout<<sp->_month<<endl;
cout<<sp->_day<<endl;
return 0;
}
- 运行结果
可以看到去掉安全锁之后,每次的运行结果都是不同的.这就是两个线程抢占式执行的结果
std::shared_ptr 的循环引用
struct ListNode
{
int _data;
shared_ptr<ListNode> _prev;
shared_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl; }
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
-
循环引用分析
-
1.node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
-
2.node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
-
3.node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
-
4.也就是说_next析构了,node2就释放了。
-
5.也就是说_prev析构了,node1就释放了。
-
6.但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放
- 解决方案
// 解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
// 原理就是,node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和_prev不会增加node1和node2的引用计数。
struct ListNode
{
int _data;
weak_ptr<ListNode> _prev;
weak_ptr<ListNode> _next;
~ListNode(){ cout << "~ListNode()" << endl;}
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
- 如果不是new出来的对象如何通过智能指针管理呢?其实shared_ptr设计了一个删除器来解决这个问题.
- 仿函数的删除器
// 仿函数的删除器
template<class T>
struct FreeFunc {
void operator()(T* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
}
};
template<class T>
struct DeleteArrayFunc {
void operator()(T* ptr)
{
cout << "delete[]" << ptr << endl;
delete[] ptr;
}
};
int main()
{
FreeFunc<int> freeFunc;
shared_ptr<int> sp1((int*)malloc(4), freeFunc);
DeleteArrayFunc<int> deleteArrayFunc;
shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc);
return 0;
}
C++11和boost中智能指针的关系
- 1.C++ 98 中产生了第一个智能指针auto_ptr.
- 2.C++ boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr.
- 3.C++ TR1,引入了shared_ptr等。不过注意的是TR1并不是标准版。
- 4.C++ 11,引入了unique_ptr和shared_ptr和weak_ptr。需要注意的是unique_ptr对应boost的
scoped_ptr。并且这些智能指针的实现原理是参考boost中的实现的。
RAII扩展学习
- RAII思想除了可以用来设计智能指针,还可以用来设计守卫锁,防止异常安全导致的死锁问题。
- 守卫锁:利用RAII思想解决线程死锁,构造函数中加锁,析构函数中解锁,防止死锁的形成和资源的管理
#include <thread>
#include <mutex>
// C++11的库中也有一个lock_guard,下面的LockGuard造轮子其实就是为了学习他的原理
template<class Mutex>
class LockGuard
{
public:
LockGuard(Mutex& mtx)
:_mutex(mtx)
{
_mutex.lock();
}
~LockGuard()
{
_mutex.unlock();
}
LockGuard(const LockGuard<Mutex>&) = delete;
private:
// 注意这里必须使用引用,否则锁的就不是一个互斥量对象
Mutex& _mutex;
};
mutex mtx;
static int n = 0;
void Func()
{
for (size_t i = 0; i < 1000000; ++i)
{
LockGuard<mutex> lock(mtx);
++n;
}
}
int main()
{
int begin = clock();
thread t1(Func);
thread t2(Func);
t1.join();
t2.join();
int end = clock();
cout << n << endl;
cout <<"cost time:" <<end - begin << endl;
return 0;
}