读书人

shared_ptr:线程保险、循环引用

发布时间: 2013-10-24 18:27:21 作者: rapoo

shared_ptr:线程安全、循环引用

shared_ptr用来管理堆对象可以避免delete,但是注意shared_ptr本身是个对象因此其的声明周期、shared_ptr对象的读写操作非原子化那么在多线程环境下仍然存在很多问题,即shared_ptr对象本身的线程安全级别非100%。在多线程中访问同一个shared_ptr对象应该用mutex保护。

shared_ptr使用不当会延长锁管理的对象(假设为A)的生命周期,即有什么地方不小心留下一个shared_ptr的拷贝,那么对象A生命期无疑被延长了,因为对象计数不为0将不会被销毁。boost::bind会将函数参数拷贝一份,那么shared_ptr<T> one, boost::function<void()> fp=boost::bind(&fun,one)//这种情况会下one管理的对象生命期一定晚于fp对象。

shared_ptr可能会导致循环引用即:对象A持有一个关于B的shared_ptr,B持有一个关于A的shared_ptr,那么A、B离开作用域的时候它们的计数都不为0(因为对方持有一个shared_ptr指向自己)而谁也不肯先释放这个shared_ptr,最后造成内存泄露。解决方法是:A持有关于B的shared_ptr,B持有关于A的weak_ptr。

当一个管理对象A的shared_ptr离开作用域且计数变为0时那么对象A将在这个线程析构(这个线程可能不是创建A的线程),若析构比较费事,那么析构A的线程将被拖累,解决办法是:找个专门的线程管理所有的shared_ptr对象,并执行析构操作。

我曾犯过的一个错误:若shared_ptr管理对象A,假设对象里面有new的堆内存,在对象A销毁前(即shared_ptr计数大于0)的时候,在某个地方A通过某些操作释放了A里里面的堆内存....那么最后的结果是?不错,double free,在A存活的时候释放了A里面的部分内存,那么在shared_ptr计数为0要销毁A的时候又去释放那块内存,然后就double free了。所以记住:避免shared_ptr管理的对象直接的内存管理操作以免造成重复释放。

下面是多线程共享一个shared_ptr对象时的保护措施:

#include<iostream>#include<boost/shared_ptr.hpp>#include<boost/weak_ptr.hpp>using namespace std;using namespace boost;class B;class A{    public:        shared_ptr<B> ptr_A;        ~A(){            cout<<"~A()"<<endl;        }};class B{    public:        //shared_ptr<A> ptr_B;//当采用shared_ptr指向A时会形成循环引用,则什么都不会输出说明对象没有被析构,可怕的内存泄露....        weak_ptr<A> ptr_B;//当采用弱引用时,避免了循环引用,有输出,说明对象被析构了        ~B(){            cout<<"~B()"<<endl;        }};int main(){    shared_ptr<A> a(new A);    shared_ptr<B> b(new B);    a->ptr_A=b;    b->ptr_B=a;//若是循环引用:当a、b退出作用域的时候,A对象计数不为1(b保留了个计数呢),同理B的计数也不为1,那么对象将不会被销毁,内存泄露了...    return 0;}

若是循环引用则什么输出都没有即对象没有析构,内存泄露了。

若是B持有A的weak_ptr则输出:

~A()
~B()

读书人网 >编程

热点推荐