《ZLToolKit源码学习笔记》(5)工具模块之资源池

 系列文章目录

《ZLToolKit源码学习笔记》(1)VS2019源码编译

《ZLToolKit源码学习笔记》(2)工具模块之日志功能分析

《ZLToolKit源码学习笔记》(3)工具模块之终端命令解析

《ZLToolKit源码学习笔记》(4)工具模块之消息广播器

《ZLToolKit源码学习笔记》(5)工具模块之资源池(本文)

《ZLToolKit源码学习笔记》(6)线程模块之整体框架概述

《ZLToolKit源码学习笔记》(7)线程模块之线程池组件:任务队列与线程组

《ZLToolKit源码学习笔记》(8)线程模块之线程负载计算器

《ZLToolKit源码学习笔记》(9)线程模块之任务执行器

《ZLToolKit源码学习笔记》(10)线程模块之线程池

《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文件。


目录

 系列文章目录

前言

一、使用

二、源码结构分析

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对象析构时,会回收资源。

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);

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦时小

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值