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

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

《ZLToolKit源码学习笔记》(5)工具模块之资源池  系列文章目录

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

template
class 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资源是回收还是删除。

template
shared_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
}
2.2、资源池:ResourcePool_l

管理资源,指定资源分配器、分配资源、回收资源、设置资源池大小。

template
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;
};
2.2.1、资源分配

1、如果获取到对象锁,且资源池不为空,则从池子中头部取出一个资源,如果资源池为空,则新分配一个资源
2、如果没有获取到对象锁,则直接新分配一个资源
3、将分配的资源封装成智能指针返回
备注:新分配的资源,由于要被使用,所以此处不能加入到资源池,在资源不再被使用,即智能指针释放时,才把资源添加到池子中;

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();
        }
        //第三个参数,默认指定该资源是需要回收的,可以通过quit函数修改后续是否需要回收
        return ValuePtr(ptr, _weak_self, std::make_shared(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的再次包装。

template
class 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 > pool;

以下这种方式是否可以直接让用户使用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_l pool;
pool.setSize(50);

 测试程序做如下修改后,便可以直接使用ResourcePool_l而不再用ResourcePool。

//大小为50的循环池
//ResourcePool_l pool;
std::shared_ptr> pool = std::make_shared>();
pool->setSize(50);

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5658458.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存