对象池的使用场景以及自动回收技术
对象池
在编程中,对象动我们经常会涉及到对象的池的场景操作,而经常的使用收技术操作模式如下图所示:创建对象->使用对象->销毁对象。

而这个对象有可能创建的及自时候会需要构建很多资源,消耗比较大,对象动 比如:在hiredis的SDK中每次都创建一个redisContext,如果需要查询,池的场景那就首先要进行网络连接。使用收技术如果一直都是及自上图的工作方式,那将会频繁的对象动创建连接,查询完毕后再释放连接。池的场景重新建立连接,使用收技术让网络的及自查询效率降低。
这个时候就可以构建一个对象池来重复利用这个对象,对象动并且一般要做到线程安全:
从对象池中获取对象,池的场景如果没有对象,使用收技术则创建一个,并返回 使用对象 使用完成对象后,将对象还回对象池
那么符合如下条件的,应该适合使用对象池技术:
有一些对象虽然创建开销比较大,但是不一定能够重复使用。服务器租用要使用对象池一定要确保对象能够重复使用。 这个对象构建的时候,有一些耗时的资源可以重复利用。比如redisContext的网络连接。又或者如果对象的频繁申请释放会带来一些其他的资源使用问题,比如内存碎片。重复利用能够提升程序的效率。 对象池的数量应该控制在能够接受的范围内,并不会无限膨胀。对象池的实现
首先介绍一下程序的样例对象Object, 其就接受一个初始化参数strInit。
class Object { public: Object(std::string strInit) : m_strInit(strInit) { std::cout << "Object()" << std::endl; } virtual ~Object() { std::cout << "~Object()" << std::endl; } private: std::string m_strInit; };先来看看对象池的类图:
ObjectPool中采用std::list作为对象池的数据结构,存储的对象采用shared_ptr包裹。 GetObject获取一个对象,传入的参数为Object需要初始化的信息,如果池子里面没有,就创建一个返回,如果有就从池子中取出一个返回。 ReturnObject 当应用程序使用完毕后,云服务器提供商调用这个方法还回对象到对象池
然后再来看看代码吧:
class ObjectPool { public: ObjectPool() { ; } ~ObjectPool() { ; } std::shared_ptr<Object> GetObject(std::string strInit) { std::shared_ptr<Object> pObject; { std::lock_guard<std::mutex> guard(m_mutex); if (!m_lObjects.empty()) { pObject = m_lObjects.front(); m_lObjects.pop_front(); } } if (!pObject) { pObject = std::make_shared<Object>(strInit); } return pObject; } void ReturnObject(std::shared_ptr<Object> pObject) { if (!pObject) return; std::lock_guard<std::mutex> guard(m_mutex); m_lObjects.push_front(pObject); } private: std::mutex m_mutex; std::list<std::shared_ptr<Object>> m_lObjects; };那么使用起来比较简单,如下所示。
ObjectPool objPool; auto pObj1 = objPool.GetObject("abc"); //操作对象完成任务 //...... objPool.ReturnObject(pObj1);但是要注意一点,有时候可能使用完了,却忘记调用ReturnObject了,这个时候是否想起了RAII技术《C++ RAII实现golang的defer》和《从lock_guard来说一说C++常用的RAII》。
那么问一问,可以实现一个自动回收的对象池吗?不需要调用者在对象使用完成后,手动将对象归还给对象池,并且你可能要问:
针对不同类型的Object,是不是可以用模板去实现更加通用的实现一个对象池 构造函数的参数列表,也可以是任意的形式自动回收的对象池
要实现自动回收的对象池,首先要了解unique_ptr和shared_ptr都可以自定义删除器,也就是说,比如当从对象池获取到的对象是用智能指针包裹的,源码下载一般默认的删除器为delete,那我们可以自义定删除器为: 将这个对象重新放回到对象池. 代码如下:
template<typename T> class ObjectPool { public: ObjectPool() { m_fObjDeleter = [&](T* pObj) { if (m_bDeconstruct) delete pObj; else { std::lock_guard<std::mutex> guard(m_mutex); m_lObjects.push_front(std::shared_ptr<T>(pObj, m_fObjDeleter)); } }; } ~ObjectPool() { m_bDeconstruct = true; } template<typename... Args> std::shared_ptr<T> GetObject(Args&&... args) { std::shared_ptr<T> pObject; { std::lock_guard<std::mutex> guard(m_mutex); if (!m_lObjects.empty()) { pObject = m_lObjects.front(); m_lObjects.pop_front(); } } if (!pObject) { pObject.reset(new T(std::forward<Args>(args)...), m_fObjDeleter); } return pObject; } void ReturnObject(std::shared_ptr<T> pObject) { if (!pObject) return; std::lock_guard<std::mutex> guard(m_mutex); m_lObjects.push_front(pObject); } private: std::function<void(T* pObj)> m_fObjDeleter; std::mutex m_mutex; std::list<std::shared_ptr<T>> m_lObjects; volatile bool m_bDeconstruct = false; };自动回收
关于自动回收,这个涉及到一个问题,是用unique_ptr还是shared_ptr呢,在这篇大牛写的文章中进行了比较详细的阐述《thinking in object pool》(链接见参考部分), 说明了应该使用unique_ptr,也看到不少人在网上转发。主要如下阐述:
因为我们需要把智能指针的默认删除器改为自定义删除器,用shared_ptr会很不方便,因为你无法直接将shared_ptr的删除器修改为自定义删除器,虽然你可以通过重新创建一个新对象,把原对象拷贝过来的做法来实现,但是这样做效率比较低。而unique_ptr由于是独占语义,提供了一种简便的方法方法可以实现修改删除器,所以用unique_ptr是最适合的。
…
这种方式需要每次都创建一个新对象,并且拷贝原来的对象,是一种比较低效的做法。
但本人自己进行了思考,认为可以做到使用shared_ptr一样实现了高效的自动回收机制。首先定义了一个m_fObjDeleter自定义deleter, 不过这种做法可能比较难理解一些,就是定义的m_fObjDeleter函数内也会调用m_fObjDeleter。当shared_ptr引用计数为0的时候,会做如下事情:
如果发现是OjbectPool调用了析构函数,则直接释放对象 如果发现OjbectPool并没有调用析构函数,则将对象放入对象池中 m_fObjDeleter = [&](T* pObj) { if (m_bDeconstruct) delete pObj; else { std::lock_guard<std::mutex> guard(m_mutex); m_lObjects.push_front(std::shared_ptr<T>(pObj, m_fObjDeleter)); } };当创建对象的时候指定自定义的deleter:
pObject.reset(new T(std::forward<Args>(args)...), m_fObjDeleter);模板支持
使用了模板可以支持通用的对象:
template<typename T> class ObjectPool { public: //...... template<typename... Args> std::shared_ptr<T> GetObject(Args&&... args) { //...... } void ReturnObject(std::shared_ptr<T> pObject) { //...... } private: std::function<void(T* pObj)> m_fObjDeleter; //..... std::list<std::shared_ptr<T>> m_lObjects; //....... };可变函数参数完美转发
不同的对象,可能使用的构造函数参数也不同,那么当调用GetObject的时候的参数要设置为可变参数,其实现如下:
template<typename... Args> std::shared_ptr<T> GetObject(Args&&... args) { std::shared_ptr<T> pObject; { std::lock_guard<std::mutex> guard(m_mutex); if (!m_lObjects.empty()) { pObject = m_lObjects.front(); m_lObjects.pop_front(); } } if (!pObject) { pObject.reset(new T(std::forward<Args>(args)...), m_fObjDeleter); } return pObject; }其他
以上对对象池的基本内容进行了阐述,那么对于对象池的实现要根据场景还有若干的细节,有些还比较重要:
是否要在启动的时候初始化指定数量的对象? 对象池的数量是否要设置一个上限或者下线 对象池重复利用,当取出来后要注意,是不是要对对象做一次reset之类的操作,防止对象上一次的调用残留数据对本地调用构成影响,这个要根据自己对象的特点去进行相应的reset操作 有时候当这个对象可能出现了特别的情况需要销毁,是否也需要考虑到? 等等参考
<<C++ Primer>>模板部分 << thinking in object pool >>: https://www.cnblogs.com/qicosmos/p/4995248.html相关文章
- 摘要:在当今游戏行业中,显卡的性能对于玩家们的游戏体验至关重要。作为一款高性能显卡,M320显卡以其卓越的性能和出色的特点备受关注。本文将深入探索M320显卡的优势,并详细介绍其游戏性能...2025-11-05
奋达音箱百度评价大揭秘(揭秘奋达音箱在百度上的用户评价与真实情况)
摘要:作为一款知名的音箱品牌,奋达音箱在市场上备受关注。然而,在用户购买前,往往会通过百度搜索相关信息来了解产品的性能和质量。本文将揭示奋达音箱在百度上的用户评价,并探究其真实情况。...2025-11-05- 摘要:随着移动设备的不断发展,对于处理器性能的需求也越来越高。作为一家领先的芯片制造商,联发科MediaTek)的八核CPU在市场上备受瞩目。本文将介绍联发科八核CPU的优势和应用场景...2025-11-05
华为荣耀6x的信号稳定性分析(华为荣耀6x信号强度如何?信号丢失情况如何?)
摘要:在现代社会中,手机已经成为人们生活中不可或缺的工具之一。手机的信号质量直接影响通话、上网以及使用其他网络功能的体验。本文将对华为荣耀6x的信号质量进行分析,特别关注其信号强度和信号...2025-11-05从零开始,轻松学习配音作品制作教程电脑使用技巧(掌握关键技巧,打造专业级配音作品)
摘要:在现今数字化时代,配音作品制作已经成为一个热门的创作形式。越来越多的人开始将自己的声音融入到各种作品中,无论是动画、影视、游戏还是广告,优秀的配音作品都能为作品增色不少。本文将以电...2025-11-05小米MLX全屏手机的颠覆力如何?(小米MLX全屏手机体验全面升级,让你爱不释手!)
摘要:作为一家备受瞩目的科技公司,小米始终致力于推出颠覆性的产品。近期,小米发布了一款全新的手机,即小米MLX全屏手机。这款手机引起了广泛的关注,究竟它在哪些方面进行了升级和改进,为用户...2025-11-05


最新评论