当前位置: 首页 > 科技观察

对象池使用场景及自动回收技术

时间:2023-03-15 15:40:32 科技观察

对象池在编程中,我们经常会涉及到对象操作,频繁的操作方式如下图所示:创建对象->使用对象->销毁对象。而且这个对象在创建的时候可能需要构建很多资源,消耗很大。比如hiredisSDK中每次都会创建一个redisContext。如需查询,必须先连接网络。如果一直是上图的工作方式,会频繁创建连接,查询完成后会释放连接。重新建立连接,降低网络的查询效率。这时候可以建立一个对象池来重用这个对象,一般要做到线程安全:从对象池中获取一个对象,如果没有对象就创建一个,对象被使用后返回对象used,returntheobject如果对象池满足以下条件,应该适合使用对象池技术:有些对象虽然创建起来代价高昂,但可能无法重用。要使用对象池必须保证对象可以被重用。在构造这个对象的时候,有一些耗时的资源是可以重用的。比如redisContext的网络连接。或者如果对象的频繁申请和释放会带来一些其他的资源占用问题,比如内存碎片。重用可以提高程序的效率。对象池的数量应该控制在可接受的范围内,不会无限扩大。对象池的实现首先介绍程序的示例对象Object,它接受一个初始化参数strInit。classObject{public:Object(std::stringstrInit):m_strInit(strInit){std::cout<<"Object()"<GetObject(std::stringstrInit){std::shared_ptrpObject;{std::lock_guardguard(m_mutex);if(!m_lObjects.empty()){pObject=m_lObjects.front();m_lObjects.pop_front();}}if(!pObject){pObject=std::make_shared(strInit);}returnpObject;}voidReturnObject(std::shared_ptrpObject){if(!pObject)return;std::lock_guardguard(m_mutex);m_lObjects.push_front(pObject);}private:std::mutexm_mutex;std::list>m_lObjects;};然后用它来比较Simple,如下图。ObjectPoolobjPool;autopObj1=objPool.GetObject("abc");//操作对象完成任务//......objPool.ReturnObject(pObj1);但是注意,有时候可能用完了,却忘了调用ReturnObject,这时候你是不是想到了RAII技术《C++ RAII实现golang的defer》和《从lock_guard来说一说C++常用的RAII》?那么请问,是否可以实现一个自动回收的对象池呢?对象使用后调用者不需要手动将对象返回到对象池,你可能会问:是否可以对不同类型的Object使用模板?实现一个对象池构造函数的参数列表更通用的实现,也可以是任何形式的自动回收的对象池。要实现一个自动回收的对象池,首先要明白unique_ptr和shared_ptr都可以自定义删除器,也就是说,比如从对象池中获取的对象用智能指针包裹时,默认删除器一般是delete,那么我们可以自定义deleter为:把这个对象放回对象池中。代码如下:);m_lObjects.push_front(std::shared_ptr(pObj,m_fObjDeleter));}};}~ObjectPool(){m_bDeconstruct=true;}templatestd::shared_ptrGetObject(Args&&...args){std::shared_ptrpObject;{std::lock_guardguard(m_mutex);if(!m_lObjects.empty()){pObject=m_lObjects.front();m_lObjects.pop_front();}}if(!pObject){pObject.reset(newT(std::forward(args)...),m_fObjDeleter);}returnpObject;}voidReturnObject(std::shared_ptrpObject){if(!pObject)返回;std::lock_guardguard(m_mutex);m_lObjects.push_front(pObject);}private:std::functionm_fObjDeleter;std::mutexm_mutex;std::list>m_lObjects;volatileboolm_bDeconstruct=false;};自动回收关于自动回收,这里涉及到一个问题,是使用unique_ptr还是shared_ptr,大牛写的这篇文章里面有详细的解释(参考部分链接),说明应该使用unique_ptr,看到网上很多人转发主要是这样的:因为我们需要把智能指针默认的删除器改成自定义的删除器,使用起来会很不方便shared_ptr,因为不能直接将shared_ptr的deleter修改为自定义的deleter。虽然可以通过创建一个新的对象并复制原来的对象来实现,但是这样效率比较低。又因为unique_ptr具有排他语义,提供了简单的修改删除器的方法,所以unique_ptr最合适。...这种方法需要每次都创建一个新的对象并复制原来的对象,是一种比较低效的做法。但是我自己也想过,觉得可以利用shared_ptr来实现一个高效的自动回收机制。首先定义了一个m_fObjDeleter自定义删除器,但这种方式可能比较难理解,即在定义的m_fObjDeleter函数中也会调用m_fObjDeleter。当shared_ptr引用计数为0时,会做以下事情:如果发现OjbectPool调用了析构函数,则直接释放对象如果发现OjbectPool没有调用析构函数,则将对象放入对象池m_fObjDeleter=[&](T*pObj){if(m_bDeconstruct)deletepObj;else{std::lock_guardguard(m_mutex);m_lObjects.push_front(std::shared_ptr(pObj,m_fObjDeleter));}};创建对象时指定自定义删除器:pObject.reset(newT(std::forward(args)...),m_fObjDeleter);模板支持使用模板支持普通对象:templateclassObjectPool{public://...templatestd::shared_ptrGetObject(Args&&...args){//...}voidReturnObject(std::shared_ptrpObject){//...}private:std::functionm_fObjDeleter;//.....std::list>m_lObjects;//.......};可变函数参数完美转发不同的对象,可能使用不同的构造函数参数,所以在调用GetObject时,需要将参数设置为可变参数,其实现如下:templatestd::shared_ptrGetObject(Args&&...args){std::shared_ptrpObject;{std::lock_guardguard(m_mutex);if(!m_lObjects.empty()){pObject=m_lObjects.front();m_lObjects.pop_front();}}if(!pObject){pObject.reset(newT(std::forward(args)...),m_fObjDeleter);}returnpObject;}Others上面已经介绍了对象池的基本内容,所以对象池的实现要看场景,还有一些细节,其中一些比较重要:什么时候初始化指定数量的对象?是否需要设置对象池数量上限或复用离线对象池?取出来的时候要注意是否需要对该对象进行reset等操作,防止该对象被上次调用。残留数据对本地通话有影响。这就需要根据对象的特性进行相应的复位操作。有时候当对象可能有特殊情况需要销毁的时候,你是不是也需要考虑一下呢?等待参考<>模板部分<<对象池中的思考>>:https://www.cnblogs.com/qicosmos/p/4995248.html