C++ 对象池

对象池

概念

对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少频繁创建对象所占用的内存空间和初始化时间。

对象池的用户可以从池子中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁。对象池是一个特殊的工厂对象,对象池模式就是单例模式加享元模式。

享元模式(Flyweight Pattern)是一种结构型设计模式,它用于减少创建对象的数量,从而降低内存的使用。享元模式通过共享尽可能多的相似对象来实现这一点。

适用场景

对象池模式主要适用于以下应用场景。

  1. 资源受限的场景。比如,不需要可伸缩性的环境(CPU\内存等物理资源有限),CPU性能不够强劲,内存比较紧张,垃圾收集,内存抖动会造成比较大的影响,需要提高内存管理效率, 响应性比吞吐量更为重要。
  2. 在内存中数量受限的对象。
  3. 创建成本高的对象,可以考虑池化。
  4. 在需要快速响应的系统中,对象池可以减少等待对象创建的时间,从而提高系统的响应性。

常见的使用对象池的场景有在使用Socket时的各种连接池、线程池、数据库连接池等。
线程池也算特殊的对象池

实现方式

对象池的使用:

  1. 从对象池中获取对象,如果没有对象,则创建一个,并返回
  2. 使用对象
  3. 使用完成对象后,将对象还回对象池
    image

实现对象池时注意的点:

  1. 对象的自动回收
  2. 线程安全
  3. 对象池重复利用时进行reset,保持对象的一致性

对象池的实现:

  1. 实现使用shared_ptr或者unique_ptr。
  2. 实现采取new和delete,需要自己去检查什么对象需要回收

无法直接将shared_ptr的删除器修改为自定义删除器,所以在使用shared_ptr时,需要一开始就定义回收函数,并把创建都委托给对象池
使用unique_ptr时,可以很方便的修改删除器

缺点和可以优化的点

对象池的缺点:

  1. 对象池在使用的时候,如果不加以控制,那么会增加内存消耗
    比如仅在某个时间段突然需要大量对象,其他时间只用少量对象
    此时对象池不够,需要添加更多的对象。而当增加完对象后,大量对象长时间存在内存里没有被使用,就导致了内存消耗。

未来优化:

  1. 可以在对象池初始化时传入工厂对象,用工厂来进行创建,而不是简单的new

示例代码

代码中需要注意使用对象池时,需要对象创建时没有入参,必须实现清理函数cleanup,以保证放入对象池中对象是干净可以复用的。
包含测试代码

#include <iostream>
#include <vector>
#include <functional>
#include <mutex>
#include <memory>
#include <atomic>
#include <thread>

template <typename T, typename = void>
struct has_cleanup : std::false_type {};

template <typename T>
struct has_cleanup<T, std::void_t<decltype(std::declval<T>().cleanup())>> : std::true_type {};

// 对象池
// 可以结合单例模式使用
template<typename T>
class ObjectPool
{
private:
    int maxsize_;
    int minsize_;
    int count_;
    std::mutex mutex_;
    std::vector<std::shared_ptr<T>> obj_list_;
    std::function<void(T* obj_ptr)> obj_destroy_func_;
    std::atomic_bool is_destructed_;
    
public:
    ObjectPool(int minSize, int maxSize)
    {
        static_assert(has_cleanup<T>::value, "T must have a cleanup() function");
        maxsize_ = maxSize;
        minsize_ = minSize;
        obj_list_.reserve(maxSize);

        obj_destroy_func_ = [&](T* obj_ptr)
        {
            if(is_destructed_ == true)
            {
                obj_ptr->cleanup();
                delete obj_ptr;
            }
            else
            {
                // 加入回收池
                obj_ptr->cleanup();
                std::lock_guard<std::mutex> lock(mutex_);
                obj_list_.push_back(std::shared_ptr<T>(obj_ptr, obj_destroy_func_));
            }
        }; 
        for(int i = 0; i < minsize_; i++)
        {
            obj_list_.emplace_back(new T(), obj_destroy_func_);
        }
        count_ = minsize_;
    }
    ~ObjectPool()
    {
        is_destructed_ = true;
    }
    
    std::shared_ptr<T> GetObject()
    {
        std::shared_ptr<T> result;
        std::lock_guard<std::mutex> lock(mutex_);
        if(obj_list_.empty())
        {
            if(count_ < maxsize_)
            {
                count_++;
                result = std::shared_ptr<T>(new T(), obj_destroy_func_);
            }
            else
            {
                // throw std::runtime_error("ObjectPool is full");
                std::cout << "ObjectPool is full" << std::endl;
                return nullptr;
            }
        }
        else
        {
            result = obj_list_.back();
            obj_list_.pop_back();
        }
        return result;
    }
};


class test
{
public:
    test(): num_{999} {}
    void init(int num)
    {
        num_ = num;
    }
    int num_;
    void cleanup()
    {
        std::cout << "cleanup: " << num_ << std::endl;
    }
};

int main()
{
    
    {
        ObjectPool<test> pool(5, 10);
        for(int i = 0; i < 3; i++)
        {
            std::shared_ptr<test> obj_ptr = pool.GetObject();
            if(obj_ptr != nullptr)
            {
                obj_ptr->init(i);
            }
            else
            {
                std::cout << "GetObject failed" << std::endl;
            }
        }
    }
    
    
    // std::this_thread::sleep_for(std::chrono::seconds(10));
    std::cout << "------------------------------------" << std::endl;
    return 0;
}


参考链接
https://zhuanlan.zhihu.com/p/437751056
https://blog.csdn.net/CJF_iceKing/article/details/119982775
https://www.cnblogs.com/qicosmos/p/4995248.html

posted @   227569hy  阅读(324)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示