《ZLToolKit源码学习笔记》(1)VS2019源码编译
《ZLToolKit源码学习笔记》(2)工具模块之日志功能分析
《ZLToolKit源码学习笔记》(3)工具模块之终端命令解析
《ZLToolKit源码学习笔记》(4)工具模块之消息广播器
《ZLToolKit源码学习笔记》(5)工具模块之资源池(本文)
前言
资源池相关功能对应ResourcePool.h文件。
目录
系列文章目录
前言
一、使用
二、源码结构分析
2.1、对象封装智能指针:shared_ptr_imp
2.2、资源池:ResourcePool_l
2.2.1、资源分配
2.2.2、资源回收
2.2.3、设置资源池的最大个数
2.3、对象池封装类:ResourcePool
一、使用
使用示例参见test_resourcePool.cpp文件。资源池中的对象被获取使用后,直到使用完成回收后才能被下一个使用者获取到。
二、源码结构分析
该部分功能在ResourcePool.h中实现,由shared_ptr_imp、ResourcePool_l、ResourcePool三个类组成。
2.1、对象封装智能指针:shared_ptr_imp智能指针实现类,管理资源的自动回收。每次要获取一个C资源对象到外部使用时,都先把该资源通过shared_ptr_imp进行封装,shared_ptr_imp对象析构时,会回收资源。
templateclass shared_ptr_imp : public std::shared_ptr { public: shared_ptr_imp() {} shared_ptr_imp(C *ptr, const std::weak_ptr > &weakPool, std::shared_ptr quit, const function &on_recycle); void quit(bool flag = true){ if(_quit){//此处是判断指针是否有效,而不是判断_quit的值是否为true *_quit = flag; } } private: std::shared_ptr _quit; };
shared_ptr_imp的有参构造函数中,使用lambda自定义了自身析构时的删除器,该删除器决定了是删除C对象还是将C对象重新添加到对象池中。如下,可以看到,如果对象池还在使用,并且用户在资源没有被使用时不想删除资源,即回收资源(指定quit为false),那么就会重新将该资源回收到对象池中,否则直接删除资源。此处,使用std::move将quit转为右值引用,通过_quit的移动构造函数转移了对象。lambda中捕获的quit即变成_quit,后续通过void quit(bool flag = true)函数可以修改_quit的值来决定资源是否被删除。
所以,shared_ptr_imp对象的构造和析构次数和C资源的构造析构次数是不一样的,两者没有关系,由用户指定C资源是回收还是删除。
template2.2、资源池:ResourcePool_lshared_ptr_imp ::shared_ptr_imp(C *ptr, const std::weak_ptr > &weakPool, std::shared_ptr quit, const function &on_recycle) : //lambda捕获quit后(值捕获,发生拷贝),quit的引用计数变为2,函数退出后,引用计数变为1(形参释放) //quit被转移到了_quit中,lambda中后续捕获的值即就是_quit的值,_quit的变更在void quit(bool flag = true)函数中 shared_ptr (ptr, [weakPool, quit, on_recycle](C *ptr) { if (on_recycle) { on_recycle(ptr); } auto strongPool = weakPool.lock(); if (strongPool && !(*quit)) { //循环池还在并且不放弃放入循环池 strongPool->recycle(ptr); } else { delete ptr; //quit为true,则删除该对象 } }), _quit(std::move(quit)) { //以下为自行添加的测试代码 cout << "quit的引用计数:" << quit.use_count() << " " << _quit.use_count() << endl;// 0 2 }
管理资源,指定资源分配器、分配资源、回收资源、设置资源池大小。
template2.2.1、资源分配class ResourcePool_l: public enable_shared_from_this > { public: typedef shared_ptr_imp ValuePtr; friend class shared_ptr_imp ; friend class ResourcePool ; ResourcePool_l() { _allotter = []()->C* { return new C(); }; } #if defined(SUPPORT_DYNAMIC_TEMPLATE) template ResourcePool_l(ArgTypes &&...args) { _allotter = [args...]()->C* { return new C(args...); }; } #endif //defined(SUPPORT_DYNAMIC_TEMPLATE) ~ResourcePool_l(){ _objs.for_each([](C *ptr){ delete ptr; }); } private: void setup(){ _weak_self = this->shared_from_this(); } private: size_t _poolsize = 8; List _objs;//资源列表 function _allotter;//资源分配器 atomic_flag _busy{false}; weak_ptr _weak_self; };
1、如果获取到对象锁,且资源池不为空,则从池子中头部取出一个资源,如果资源池为空,则新分配一个资源
2、如果没有获取到对象锁,则直接新分配一个资源
3、将分配的资源封装成智能指针返回
备注:新分配的资源,由于要被使用,所以此处不能加入到资源池,在资源不再被使用,即智能指针释放时,才把资源添加到池子中;
ValuePtr obtain(const function2.2.2、资源回收&on_recycle = nullptr) { C *ptr; auto is_busy = _busy.test_and_set(); if (!is_busy) { //获取到锁 if (_objs.size() == 0) { ptr = _allotter() } else { ptr = _objs.front(); _objs.pop_front(); } _busy.clear(); } else { //未获取到锁 ptr = _allotter(); } //第三个参数,默认指定该资源是需要回收的,可以通过quit函数修改后续是否需要回收 return ValuePtr(ptr, _weak_self, std::make_shared(false), on_recycle); }
1、如果获取到对象锁,且当前池中资源数量已达到上线,则不回收该资源,直接释放,否则回收。
2、如果没有获取到对象锁,则不回收该资源,直接释放。
void recycle(C *obj) { auto is_busy = _busy.test_and_set(); if (!is_busy) { //获取到锁 if (_objs.size() >= _poolsize) { delete obj; } else { _objs.emplace_back(obj); } _busy.clear(); } else { //未获取到锁 delete obj; } }2.2.3、设置资源池的最大个数
默认为8个。虽然资源池中的资源个数最多只能有_poolsize个,但是用户实际在使用的资源个数是没有限制的。
从obtain函数可以看出,如果用户一直调用该函数分配资源,且不释放,那么肯定会超过_poolsize;
从recycle函数可以看出,当资源池容量达到_poolsize后,其余待释放的资源是不会被回收的。
void setSize(size_t size) { _poolsize = size; }2.3、对象池封装类:ResourcePool
ResourcePool_l对象是该类唯一的成员变量,该类实际上就是对ResourcePool_l的再次包装。
templateclass ResourcePool { public: typedef shared_ptr_imp ValuePtr; ResourcePool() { pool.reset(new ResourcePool_l ()); pool->setup(); } #if defined(SUPPORT_DYNAMIC_TEMPLATE) template ResourcePool(ArgTypes &&...args) { pool = std::make_shared >(std::forward(args)...); pool->setup(); } #endif //defined(SUPPORT_DYNAMIC_TEMPLATE) void setSize(size_t size) { pool->setSize(size); } ValuePtr obtain(const function &on_recycle = nullptr) { return pool->obtain(on_recycle); } private: std::shared_ptr > pool; }; } #endif
因为在ResourcePool_l中,需要将自身传递到shared_ptr_imp的构造函数中,这里用到了shared_from_this()来获取一个shared_ptr实例。
shared_from_this()有两个使用限制:
1、不能在自身的构造函数中调用;
2、只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this 。
所以作者提供了ResourcePool类,唯一的成员变量:std::shared_ptr
以下这种方式是否可以直接让用户使用ResourcePool_l而不再用ResourcePool呢?在obtain函数中调用shared_from_this。
ValuePtr obtain(const function&on_recycle = nullptr) { C *ptr; auto is_busy = _busy.test_and_set(); if (!is_busy) { //获取到锁 if (_objs.size() == 0) { ptr = _allotter() } else { ptr = _objs.front(); _objs.pop_front(); } _busy.clear(); } else { //未获取到锁 ptr = _allotter(); } //在此处调用shared_from_this if(!_weak_self.lock())_weak_self = this->shared_from_this(); //第三个参数,默认指定该资源是需要回收的,可以通过quit函数修改后续是否需要回收 return ValuePtr(ptr, _weak_self, std::make_shared(false), on_recycle); }
这种方式test_resourcePool.cpp测试程序运行时会崩溃,原因是违背了上边的第二条: 只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this。
pool没有用std::shared_ptr管理。
//大小为50的循环池 ResourcePool_lpool; pool.setSize(50);
测试程序做如下修改后,便可以直接使用ResourcePool_l而不再用ResourcePool。
//大小为50的循环池 //ResourcePool_lpool; std::shared_ptr > pool = std::make_shared >(); pool->setSize(50);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)