系列文章目录
《ZLToolKit源码学习笔记》(1)VS2019源码编译
《ZLToolKit源码学习笔记》(2)工具模块之日志功能分析
《ZLToolKit源码学习笔记》(3)工具模块之终端命令解析
《ZLToolKit源码学习笔记》(4)工具模块之消息广播器
《ZLToolKit源码学习笔记》(5)工具模块之资源池(本文)
《ZLToolKit源码学习笔记》(6)线程模块之整体框架概述
《ZLToolKit源码学习笔记》(7)线程模块之线程池组件:任务队列与线程组
《ZLToolKit源码学习笔记》(8)线程模块之线程负载计算器
《ZLToolKit源码学习笔记》(9)线程模块之任务执行器
《ZLToolKit源码学习笔记》(11)线程模块之工作线程池WorkThreadPool
《ZLToolKit源码学习笔记》(12)事件轮询模块之整体框架概述
《ZLToolKit源码学习笔记》(13)事件轮询模块之管道的简单封装
《ZLToolKit源码学习笔记》(14)事件轮询模块之定时器
《ZLToolKit源码学习笔记》(15)事件轮询模块之事件轮询器EventPoller
《ZLToolKit源码学习笔记》(16)网络模块之整体框架概述
《ZLToolKit源码学习笔记》(17)网络模块之基础接口封装类SockUtil
《ZLToolKit源码学习笔记》(18)网络模块之Buffer缓存
《ZLToolKit源码学习笔记》(19)网络模块之套接字封装
《ZLToolKit源码学习笔记》(20)网络模块之TcpServer
《ZLToolKit源码学习笔记》(21)网络模块之TcpClient与Session
《ZLToolKit源码学习笔记》(22)网络模块之UdpServer
前言
资源池相关功能对应ResourcePool.h文件。
目录
一、使用
使用示例参见test_resourcePool.cpp文件。资源池中的对象被获取使用后,直到使用完成回收后才能被下一个使用者获取到。
二、源码结构分析
该部分功能在ResourcePool.h中实现,由shared_ptr_imp、ResourcePool_l、ResourcePool三个类组成。
2.1、对象封装智能指针:shared_ptr_imp
智能指针实现类,管理资源的自动回收。每次要获取一个C资源对象到外部使用时,都先把该资源通过shared_ptr_imp进行封装,shared_ptr_imp对象析构时,会回收资源。
template<typename C>
class shared_ptr_imp : public std::shared_ptr<C> {
public:
shared_ptr_imp() {}
/**
* 构造智能指针
* @param ptr 裸指针
* @param weakPool 管理本指针的循环池
* @param quit 对接是否放弃循环使用
*/
shared_ptr_imp(C *ptr,
const std::weak_ptr<ResourcePool_l<C> > &weakPool,
std::shared_ptr<atomic_bool> quit,
const function<void(C *)> &on_recycle);
/**
* 放弃或恢复回到循环池继续使用,该函数设置的_quit影响的是shared_ptr_imp构造函数中自定义的lambda删除器
* @param flag
*/
void quit(bool flag = true){
if(_quit){//此处是判断指针是否有效,而不是判断_quit的值是否为true
*_quit = flag;
}
}
private:
std::shared_ptr<atomic_bool> _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资源是回收还是删除。
/* 自定义了删除器,删除器处理逻辑如下:
1、如果对象池还在,且明确要回收该对象(quit为false),则调用对象池的回收函数
2、否则,直接删除该对象,不再放入对象池中 */
template<typename C>
shared_ptr_imp<C>::shared_ptr_imp(C *ptr,
const std::weak_ptr<ResourcePool_l<C> > &weakPool,
std::shared_ptr<atomic_bool> quit,
const function<void(C *)> &on_recycle) :
//lambda捕获quit后(值捕获,发生拷贝),quit的引用计数变为2,函数退出后,引用计数变为1(形参释放)
//quit被转移到了_quit中,lambda中后续捕获的值即就是_quit的值,_quit的变更在void quit(bool flag = true)函数中
shared_ptr<C>(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
}
2.2、资源池:ResourcePool_l
管理资源,指定资源分配器、分配资源、回收资源、设置资源池大小。
template<typename C>
class ResourcePool_l: public enable_shared_from_this<ResourcePool_l<C> > {
public:
typedef shared_ptr_imp<C> ValuePtr;
friend class shared_ptr_imp<C>;
friend class ResourcePool<C>;
ResourcePool_l() {
_allotter = []()->C* {
return new C();
};
}
#if defined(SUPPORT_DYNAMIC_TEMPLATE)
template<typename ...ArgTypes>
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<C*> _objs;//资源列表
function<C*(void)> _allotter;//资源分配器
atomic_flag _busy{false};
weak_ptr<ResourcePool_l > _weak_self;
};
2.2.1、资源分配
1、如果获取到对象锁,且资源池不为空,则从池子中头部取出一个资源,如果资源池为空,则新分配一个资源
2、如果没有获取到对象锁,则直接新分配一个资源
3、将分配的资源封装成智能指针返回
备注:新分配的资源,由于要被使用,所以此处不能加入到资源池,在资源不再被使用,即智能指针释放时,才把资源添加到池子中;
ValuePtr obtain(const function<void(C *)> &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<atomic_bool>(false), on_recycle);
}
2.2.2、资源回收
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的再次包装。
/**
* 循环池,注意,循环池里面的对象不能继承enable_shared_from_this!
* @tparam C
*/
template<typename C>
class ResourcePool {
public:
typedef shared_ptr_imp<C> ValuePtr;
ResourcePool() {
pool.reset(new ResourcePool_l<C>());
pool->setup();
}
#if defined(SUPPORT_DYNAMIC_TEMPLATE)
template<typename ...ArgTypes>
ResourcePool(ArgTypes &&...args) {
pool = std::make_shared<ResourcePool_l<C> >(std::forward<ArgTypes>(args)...);
pool->setup();
}
#endif //defined(SUPPORT_DYNAMIC_TEMPLATE)
void setSize(size_t size) {
pool->setSize(size);
}
ValuePtr obtain(const function<void(C *)> &on_recycle = nullptr) {
return pool->obtain(on_recycle);
}
private:
std::shared_ptr<ResourcePool_l<C> > pool;
};
} /* namespace toolkit */
#endif /* UTIL_RECYCLEPOOL_H_ */
因为在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<C> > pool;
以下这种方式是否可以直接让用户使用ResourcePool_l而不再用ResourcePool呢?在obtain函数中调用shared_from_this。
ValuePtr obtain(const function<void(C *)> &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<atomic_bool>(false), on_recycle);
}
这种方式test_resourcePool.cpp测试程序运行时会崩溃,原因是违背了上边的第二条: 只允许在先前已被std::shared_ptr 管理的对象上调用 shared_from_this。
pool没有用std::shared_ptr管理。
//大小为50的循环池
ResourcePool_l<string_imp> pool;
pool.setSize(50);
测试程序做如下修改后,便可以直接使用ResourcePool_l而不再用ResourcePool。
//大小为50的循环池
//ResourcePool_l<string_imp> pool;
std::shared_ptr<ResourcePool_l<string_imp>> pool = std::make_shared<ResourcePool_l<string_imp>>();
pool->setSize(50);