指针非常强大,是c++的精华地点,但用裸指针总有点心惊肉跳,怕一个不小心就引起内存问题,排查起来就相当费时吃力了。裸指针有哪些问题:
- 忘记开释资源,导致资源泄露(常产生内存走漏问题)
- 同一资源开释屡次,导致开释野指针,程序溃散
- 已写了开释资源的代码,可是由于程序逻辑不满足条件,导致开释- 资源的代码未被履行到
- 代码运转过程中产生异常,导致开释资源的代码未被履行到 智能指针便是用来处理这些问题的,它能让开发者不关心资源的开释,由于智能指针能够主动完结资源的开释,它能确保不论代码怎样跑,资源终究都会开释
智能指针本质上是一个泛型类,类中包含传入的指针,当开发者初始化一个智能指针时,此刻智能指针是在栈上初始化,假如智能指针一旦出了效果域,它就会被收回,履行智能指针的析构函数,智能指针则可趁机决议是否开释内部的指针资源。
智能指针的基本原理,便是“栈上的目标出效果域会主动析构”,排出萝卜带出泥,自己被析构了,顺便把实在的指针给开释了
一、自界说智能指针
假如让咱们自己来完结一个智能指针,咱们该怎样完结呢?
- 智能收回,假如智能指针被收回,需求判别,实在的指针是否要被收回
- 重界说操作符,*号以及 → 等,智能指针和裸指针的运用体验相同
- 指针计数,假如有多个指针指向同一个目标,应该经过计数处理指针,由于一个智能指针到生命周期了,但此目标还被其它智能指针引证着,所以还不能收回目标
依照这几个要求,咱们写一个相当简略的智能指针
template<typename T>
class smart_ptr{
private:
int* m_count;
T* m_ptr;
public:
smart_ptr():m_ptr(nullptr), m_count(nullptr){};
smart_ptr(T* ptr):m_ptr(ptr){
m_count = new int(1);
};
~smart_ptr() {
(*m_count)--;
cout << "smart ptr delete count = " << *m_count << endl;
if ((*m_count) == 0) {
delete m_ptr;
delete m_count;
}
};
smart_ptr(smart_ptr& ptr): m_ptr(ptr.m_ptr), m_count(ptr.m_count) {
(*m_count)++;
}
smart_ptr& operator=(smart_ptr& ptr){
m_ptr = ptr.m_ptr;
m_count = ptr.m_count;
(*m_count)++;
return *this;
}
int getCount(){return (*m_count);};
T& operator*(){
return *m_ptr;
}
T* operator->(){
return m_ptr;
}
};
void test_smartptr(){
{
smart_ptr<stu> ptr(new stu);
smart_ptr<stu> ptr2(ptr);
smart_ptr<stu> ptr3;
ptr3 = ptr2;
ptr->name_ptr = "tom";
cout << ptr->name_ptr << " count = " << ptr.getCount() << " ptr.count = " << ptr.getCount() << " ptr3.count = " << ptr3.getCount() << endl;
}
}
履行对应测试办法,log如下:
tom count = 3 ptr.count = 3 ptr3.count = 3
smart ptr delete count = 2
smart ptr delete count = 1
smart ptr delete count = 0
delete stu
上面的代码现已初步完结一个智能指针,当两个智能指针指向同一个目标时,ptr收回时,由于count值为1,阐明外边还有一个智能指针在引证此目标,因而不能收回,等到ptr2收回时,count为0了,才收回相应目标。但上面的示例代码还是比较简略,由于没有考虑多线程状况,在源码中是经过cas操作来完结线程安全的。
另外此处还有一个小细节,m_count为什么是一个指针?假如m_count仅仅一个int值,那么在履行仿制结构函数时,只能更改本身的m_count值,其它智能指针的m_count值无法更改或许改得比较费事。由于用其它智能指针赋值生成一个新的智能指针时,新旧两个智能指针的m_count值都应该加1,所以,用指针就便利多了,新旧两个智能指针的m_count指向同一块内存区域,这样,改了一处,另一处也就更改了。
比对自界说智能指针的相关代码,咱们先来看看shared_ptr和weak_ptr的用法
二、智能指针的用法
shared_ptr和weak_ptr,都是带引证计数的智能指针。同之前的自界说智能指针一样,当答应多个智能指针指向同一个资源的时候,每一个智能指针都会给资源的引证计数加1,当一个智能指针析构时,相同会使资源的引证计数减1,这样最终一个智能指针把资源的引证计数从1减到0时,就阐明该资源能够开释了。
shared_ptr,强智能指针,能够多个shared_ptr指向同一个资源,也是运用得最普遍的智能指针,但它有个问题,它不能处理循环引证问题。
class B;
class A{
public:
A(){cout << "create a" << endl;}
~A(){cout << "destroy a" << endl;}
shared_ptr<B> _ptrb;
};
class B{
public:
B(){cout << "create b" << endl;}
~B(){cout << "destroy a" << endl;}
shared_ptr<A> _ptra;
};
void test_loop_refrence(){
shared_ptr<A> ptra(new A);
shared_ptr<B> ptrb(new B);
ptra->_ptrb = ptrb;
ptrb->_ptra = ptra;
cout << ptra.use_count() << endl;
cout << ptrb.use_count() << endl;
}
日志输出:
create a
create a
2
2
循环引证下,出main函数效果域,ptra和ptrb两个局部目标析构,分别给A目标和 B目标的引证计数从2减到1,达不到开释A和B的条件(开释的条件是 A和B的引证计数为0),因而形成两个new出来的A和B目标无法开释, 导致内存泄露,这个问题便是“强智能指针的穿插引证(循环引证)问题”。weak_ptr则能够处理这种问题,将A 和 B 类中的智能指针改为weak_ptr,即可处理上述问题
弱智能指针weak_ptr区别于shared_ptr之处在于:
- weak_ptr不会改变资源的引证计数,仅仅一个调查者的角色,经过调查shared_ptr来判定资源是否存在
- weak_ptr持有的引证计数,不是资源的引证计数,而是同一个资源的调查者的计数
- weak_ptr没有供给常用的指针操作,无法直接访问资源,需求先经过lock办法提升为shared_ptr强智能指针,才干访问资源
一般来说,运用智能指针能够运用以下原则:界说目标时,用强智能指针shared_ptr,在其它当地引证目标时,运用弱智能指针weak_ptr。
三、源码剖析
这儿剖析的源码来自于gcc-10.2.0,gcc-10.2.0/libstdc++-v3/include/tr1/shared_ptr.h shared_ptr和weak_ptr牵涉有好几个不同的类,先来看看它们牵涉哪些类以及相应的联系:
shared_ptr承继__shared_ptr,而__shared_ptr中有两个成员变量:
- _Tp*,实在的指针,指向要操作的数据
- __shared_count,用于计数相关的逻辑
weak_ptr也是相同的结构。两边的count成员变量都引证着一个_Sp_counted_base指针,所以,先来看看_Sp_counted_base
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base
: public _Mutex_base<_Lp>
{
public:
_Sp_counted_base()
: _M_use_count(1), _M_weak_count(1) { }
virtual
~_Sp_counted_base() // nothrow
{ }
// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void
_M_dispose() = 0; // 当use count为0时,开释实在指针
// Called when _M_weak_count drops to zero.
virtual void
_M_destroy() // 当weak count为0时,毁掉自己
{ delete this; }
virtual void*
_M_get_deleter(const std::type_info&) = 0;
void
_M_add_ref_copy()
{ __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); } //添加use count
void
_M_add_ref_lock(); //从weak_ptr变成shared_ptr时需求调用的办法
void
_M_release() // nothrow
{
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1){
_M_dispose();
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1){
_M_destroy();
}
}
}
void
_M_weak_add_ref() // nothrow 添加weak count
{ __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
void
_M_weak_release() // nothrow
{
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
if (_Mutex_base<_Lp>::_S_need_barriers)
{
// See _M_release(),
// destroy() must observe results of dispose()
__atomic_thread_fence (__ATOMIC_ACQ_REL);
}
_M_destroy();
}
}
long
_M_get_use_count() const // nothrow 回来use count数
{
return const_cast<const volatile _Atomic_word&>(_M_use_count);
}
private:
_Sp_counted_base(_Sp_counted_base const&);
_Sp_counted_base& operator=(_Sp_counted_base const&);
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
};
_M_use_count,代表着有多少个shared_ptr指向了引证数据,而_M_weak_count则代表了weak_ptr的个数。
当_M_release办法时,假如_M_use_count等于1,自减之后等于0,则表明没有shared_ptr再指向相应资源了,则要收回掉相应的资源,即那个办理的实在的指针。假如_M_weak_count自减之后等于0,则需求调用_M_destroy办法,毁掉自己。
virtual void
_M_dispose() // nothrow
{ _M_del(_M_ptr); }
_M_dispose办法的完结在_Sp_counted_base_impl 中,删去对应指针
接下来一同看看__shared_count类的源码(有删减,将一些要点杰出)
template<_Lock_policy _Lp = __default_lock_policy>
class __shared_count
{
public:
__shared_count()
: _M_pi(0) // nothrow
{ }
//析构函数,履行_Sp_counted_base的release办法,use_count自减,判别是否要删去办理的指针
//weak_count自减,判别是否要删去 _Sp_counted_base 本身的指针,而 _Sp_counted_base 也是以指针方式保存在 __shared_count中
~__shared_count() // nothrow
{
if (_M_pi != 0)
_M_pi->_M_release();
}
//仿制结构函数,履行_M_add_ref_copy办法,自己以及被仿制的目标,use_count都会自增一
__shared_count(const __shared_count& __r)
: _M_pi(__r._M_pi) // nothrow
{
if (_M_pi != 0)
_M_pi->_M_add_ref_copy();
}
long
_M_get_use_count() const // nothrow
{ return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; }
_Sp_counted_base<_Lp>* _M_pi;
};
从源码中能够看出,当履行仿制结构函数时,会履行_Sp_counted_base的_M_add_ref_copy办法,use_count将会自增。当履行析构函数时,将会履行_Sp_counted_base的release办法,而release办法中将会查看use_count和weak_count,删去办理的指针或_Sp_counted_base本身。_Sp_counted_base正好也是以指针方式存在于__shared_count中,履行_Sp_counted_base的destroy办法时,_Sp_counted_base的裸指针将被删去,不会有走漏。
整个思路和前文榜首部分的自界说智能指针一模一样
接下来咱们再看看__weak_count的源码
template<_Lock_policy _Lp>
class __weak_count
{
public:
__weak_count()
: _M_pi(0) // nothrow
{ }
//仿制构建函数,仅仅调用_M_weak_add_ref,自增weak_count
__weak_count(const __shared_count<_Lp>& __r)
: _M_pi(__r._M_pi) // nothrow
{
if (_M_pi != 0)
_M_pi->_M_weak_add_ref();
}
//仿制构建函数,仅仅调用_M_weak_add_ref,自增weak_count
__weak_count(const __weak_count<_Lp>& __r)
: _M_pi(__r._M_pi) // nothrow
{
if (_M_pi != 0)
_M_pi->_M_weak_add_ref();
}
~__weak_count() // nothrow
{
if (_M_pi != 0)
_M_pi->_M_weak_release();
}
long
_M_get_use_count() const // nothrow
{ return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; }
_Sp_counted_base<_Lp>* _M_pi;
};
与__shared_count不同的是,履行仿制结构函数时,仅仅自增weak_count的值。履行析构函数时,履行_Sp_counted_base的_M_weak_release办法,_M_weak_release办法会判别weak_count数量,决议是否开释_Sp_counted_base的指针,__weak_count的析构函数并不会开释办理的实在的指针。
接下来看看__shared_ptr类
//运用__weak_count作参数的仿制结构函数,意味着此智能指针要转化为shared_ptr,不再是weak_ptr
//所以,需求调用_M_add_ref_lock办法,自增use_count
template<_Lock_policy _Lp>
inline
__shared_count<_Lp>::
__shared_count(const __weak_count<_Lp>& __r)
: _M_pi(__r._M_pi)
{
if (_M_pi != 0)
_M_pi->_M_add_ref_lock();
else
__throw_bad_weak_ptr();
}
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
public:
typedef _Tp element_type;
//默许结构函数
__shared_ptr()
: _M_ptr(0), _M_refcount() // never throws
{ }
//运用__shared_ptr作为参数的仿制结构函数
template<typename _Tp1>
__shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, __static_cast_tag)
: _M_ptr(static_cast<element_type*>(__r._M_ptr)),
_M_refcount(__r._M_refcount)
{ }
//运用__weak_ptr作为参数的仿制结构函数,__shared_ptr的成员变量_M_refcount是__shared_count
//而__r._M_refcount是__weak_count,__shared_count的这类仿制结构函数前最前面,它将会调用_M_add_ref_lock办法,自增use_count
template<typename _Tp1>
explicit
__shared_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
: _M_refcount(__r._M_refcount) // may throw
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
// It is now safe to copy __r._M_ptr, as _M_refcount(__r._M_refcount)
// did not throw.
_M_ptr = __r._M_ptr;
}
//模仿指针运用办法而重写的运算符函数
operator*() const // never throws
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return *_M_ptr;
}
_Tp*
operator->() const // never throws
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}
_Tp*
get() const // never throws
{ return _M_ptr; }
// Implicit conversion to "bool"
public:
long
use_count() const // never throws
{ return _M_refcount._M_get_use_count(); }
_Tp* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
};
留意__shared_ptr的几个仿制结构函数,它能够由__shared_ptr仿制,也能够由__weak_ptr结构,当由__weak_ptr结构时,履行_M_add_ref_lock办法,其实是将weak_ptr转换成了shared_ptr,一同自增use_cont
最终,一同看看__weak_ptr的代码
template<typename _Tp, _Lock_policy _Lp>
class __weak_ptr
{
public:
typedef _Tp element_type;
__weak_ptr()
: _M_ptr(0), _M_refcount() // never throws
{ }
//weak_ptr并不能直接获取办理的指针,需求经过调用lock办法,转成shared_ptr,才干获取办理的指针并且完结赋值
//而_M_refcount,依据weak_count的源码阐明,仅仅调用_M_weak_add_ref,自增weak count
template<typename _Tp1>
__weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
: _M_refcount(__r._M_refcount) // never throws
{
_M_ptr = __r.lock().get();
}
template<typename _Tp1>
__weak_ptr(const __shared_ptr<_Tp1, _Lp>& __r)
: _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws
{ __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) }
//lock办法,将weak_ptr转换成一个shared_ptr,调用shared_ptr的一个仿制结构函数
__shared_ptr<_Tp, _Lp>
lock() const // never throws
{
__try
{
return __shared_ptr<element_type, _Lp>(*this);
}
__catch(const bad_weak_ptr&)
{
return __shared_ptr<element_type, _Lp>();
}
} // XXX MT
long
use_count() const // never throws
{ return _M_refcount._M_get_use_count(); }
private:
_Tp* _M_ptr; // Contained pointer.
__weak_count<_Lp> _M_refcount; // Reference counter.
};
与__shared_ptr不同的是,__weak_ptr的仿制结构函数只会自增weak count,不会自增use count,所以完全不会影响办理的指针的开释。
综上所述,__shared_ptr具有成员变量__shared_count,而__shared_count具有成员变量,准确说是一个指针,_Sp_counted_base*,_Sp_counted_base内有两个成员变量_M_use_count和_M_weak_count,当初始化__shared_ptr时,_M_use_count自增,用其它shared_ptr来初始化一个新的shared_ptr时,则二者的_M_use_count都会加1,终究在栈内,shared_ptr析构时,会计算当前_M_use_count是否为0,假如为0,则开释办理的指针,假如_M_weak_count也为0,则将内部的成员变量指针_Sp_counted_base开释。
__weak_ptr,和上述相似,仅仅它在初始化时是_M_weak_count自增,完全不影响_M_use_count,它析构时,仍然会调用__weak_count的析构函数,即调用_Sp_counted_base的_M_weak_release办法,此办法只会判别weak_count是否为0,假如是0,则删去_Sp_counted_base指针,底子不会影响办理的实在指针。__weak_ptr经过调用lock办法可转换成__shared_ptr,其实也便是调用__shared_ptr的仿制结构函数罢了,不过use count会自增
经过这么多的解说,weak_ptr为什么能处理双循环引证的问题呢?原因还是在于weak_count的规划,不会添加use count,所以不会搅扰办理的指针收回。
而shared_ptr为什么能主动收回办理的指针呢,经过栈主动收回超出效果域的目标,收回shared_ptr时,依据use count决议是否收回办理的指针。
最终,貌似讲了半天,也没提是怎么删去办理的实在的指针。_Sp_counted_base_impl承继_Sp_counted_base,它多了两个成员变量,_M_ptr和_M_del。其实在__shared_ptr初始化时,则会去初始化__shared_count,再去初始化_Sp_counted_base_impl,__shared_ptr内办理的指针会传递给_M_ptr,而_M_del是一个担任删去指针的结构体,所以在__shared_ptr析构时,会履行_shared_count的析构,而_shared_count析构,则会履行_Sp_counted_base_impl的_M_release办法,_M_release办法中会调用_M_dispose,收回办理的指针
除了shared_ptr和weak_ptr之外,还有一个智能指针,unique_ptr,顾名思义,它便是一个原生指针绝无仅有的具有者和办理者,它不答应别的unique_ptr再占用原生指针,甚至它的仿制结构函数以及赋值函数都是不答应调用的。
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
unique_ptr(unique_ptr&&) = default;
unique_ptr& operator=(unique_ptr&&) = default;
用法:
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
std::unique_ptr<Task>taskPtr5(new Task(55));
要用它,只能经过右值赋值或许直接传原生指针才行。
unique_ptr,它的原理便是经过右值赋值,完结一人独占,由于它是一人独占,所以底子不必计数了,unique_ptr自己的生命同期到了,办理的原生指针也会跟着收回了
它的用法较其它更简略一些,在此不多做介绍,今后再讲右值的时候再讲
四、enable_shared_from_this剖析 智能指针有一个坑存在。
stu* stu_ptr = new stu("seven");
shared_ptr<stu> ptr1(stu_ptr);
shared_ptr<stu> ptr2(stu_ptr);
cout << " count1 = " << ptr1.use_count() << endl;
cout << " count2 = " << ptr2.use_count() << endl;
输出的log:
count1 = 1
count2 = 1
delete stu
delete stu
分明ptr1 和 ptr2都是办理着stu_ptr,但它们的use_count办法回来值分别为1,而不是2,导致stu_ptr将会被收回两次,程序报错。
智能指针也不智能的原因在于shared_ptr的结构办法,假如不是调用仿制结构函数,而是传入被办理的指针,那么对应的_M_refcount将会履行默许初始化办法,从前文可知,履行默许的初始化办法,那么use count将为1
template<typename _Tp1>
explicit
__shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
typedef int _IsComplete[sizeof(_Tp1)];
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
所以,想要让指向同一指针的智能指针计数正常,第二个智能指针只能用仿制结构函数,经过其它智能指针赋值才行。
回到 enable_shared_from_this,它的主要效果是供给一个函数,回来当前目标的一个shared_ptr。假如依照正常思路,得这么写:
shared_ptr<stu> getSharePtr(){
return shared_ptr<stu>(this);
}
但这样写正好踩了前面的坑,导致use_count为1,这个目标会被收回两次,肯定是不行的。从前面可知,假如要回来一个正常的智能指针,必须用其它智能指针来赋值。enable_shared_from_this便是用来处理这个问题的。
回看前面__shared_ptr的构建函数,它还调用了__enable_shared_from_this_helper办法,这个办法是干啥的呢?
template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<_Lp>& __pn,
const __enable_shared_from_this* __pe,
const _Tp1* __px)
{
if (__pe != 0)
__pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
}
假如某个目标承继__enable_shared_from_this,在构建__shared_ptr时,传入本身类型的指针,其实也能够看作是传入了__enable_shared_from_this指针,由于承继自__enable_shared_from_this,能够转换成这种指针,然后调用_M_weak_assign办法
template<typename _Tp1>
void
_M_weak_assign(_Tp1* __p, const __shared_count<_Lp>& __n) const
{ _M_weak_this._M_assign(__p, __n); }
mutable __weak_ptr<_Tp, _Lp> _M_weak_this;
private:
// Used by __enable_shared_from_this.
void
_M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount)
{
_M_ptr = __ptr;
_M_refcount = __refcount;
}
__shared_ptr<_Tp, _Lp>
shared_from_this()
{ return __shared_ptr<_Tp, _Lp>(this->_M_weak_this); }
__enable_shared_from_this中有个弱智能指针成员变量,_M_weak_this,调用__weak_ptr的_M_assign办法,其实便是初始化__weak_ptr两个成员变量,生成一个非空的__weak_ptr。
承继__enable_shared_from_this的目标,想要获取指向本身的__shared_ptr,调用shared_from_this办法即可,将一个弱智能指针_M_weak_this转换成一个强智能指针,目的就完结了。
五、shared_ptr的线程安全
智能指针的线程安全问题,与一个朴素的流程问题相关:把大象关冰箱分红几步,三步
那么,生成一个shared_ptr分红几步,两步:
- 引证计数
- 指针赋值
引证计数是线程安全的,毋庸置疑,由于引证计数采用了cas(compare and set)的原子操作。
_Atomic_word _M_use_count;
void
_M_weak_add_ref() // nothrow
{ __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
void
_M_release() // nothrow
{
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
_M_dispose();
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)
{
_M_destroy();
}
}
}
锁能够分红达观锁和失望锁,失望锁即是一般意义上的加锁,互斥同步,不论干什么,先加锁保护起来,然后操作。但加锁会导致代码运转功率变低,由于涉及到线程切换等各种事情。失望锁便是运用互斥同步的手法来确保线程安全的
达观锁,和失望锁相反,不必互斥同步,但它依赖于硬件,由于咱们需求操作和冲突检测这两个过程具备原子性,假如不考虑互斥来完结,那只能运用硬件来完结了,硬件确保一个从主观上看起来需求屡次操作的行为只经过一条处理器指令就能完结。
cas便是一种达观锁,经过原子操作来完结多线程安全地写数据
指针赋值是线程安全的吗?显着不是的,源码中没有看到任何一处与指针赋值有关的线程安全代码。所以,shared_ptr赋值操作有两个过程,但有一个过程是不安全的,那么shared_ptr便是不安全的了。哪些操作是不安全的呢?能够参阅www.boost.org/doc/libs/1_…的几个示例,假如是多个线程读shared_ptr,那肯定是安全的,但假如是多个线程写 shared_ptr,那就不安全了。
关于线程不安全的问题,用图来阐明就会更好理解了:
所以,遇到这种多线程写指针的状况,还是老老实实地加锁干活吧。值得一提的是,某些特别的场景,能够灵活运用weak_ptr来做勘探shared_ptr是否现已被收回了,不必加锁而处理部分的多线程问题。
由于weak_ptr的lock办法是经过检测 use_count值来判别shared_ptr是否现已被收回,假如没有被收回,则生成正确的shared_ptr,假如已收回,则生成一个空的shared_ptr,所以能够灵活运用weak_ptr,它能够有效地勘探shared_ptr是否还存在,然后处理部分多线程问题。
__shared_ptr<_Tp, _Lp>
lock() const // never throws
{
return expired() ? __shared_ptr<element_type, _Lp>()
: __shared_ptr<element_type, _Lp>(*this);
} // XXX MT
bool
expired() const // never throws
{ return _M_refcount._M_get_use_count() == 0; }
示例:
class Test{
private:
int* volatile m_ptr;
public:
Test() : m_ptr(new int(20)){
cout << "create test" << endl;
}
~Test(){
delete m_ptr;
m_ptr = nullptr;
cout << "delete test" << endl;
}
void show(){
cout << *m_ptr << endl;
}
};
void threadSafe(weak_ptr<Test> pw) {
std::this_thread::sleep_for(std::chrono::seconds(2));
shared_ptr<Test> ps = pw.lock();
if(ps != nullptr) {
ps->show();
}
}
void test_safy_smartptr(){
shared_ptr<Test> p(new Test);
std::thread t1(threadSafe, weak_ptr<Test>(p));
t1.join();
// t1.detach();
}