对象池
概念
对象池模式(Object Pool Pattern),是创建型设计模式的一种,将对象预先创建并初始化后放入对象池中,对象提供者就能利用已有的对象来处理请求,减少频繁创建对象所占用的内存空间和初始化时间。
对象池的用户可以从池子中取得对象,对其进行操作处理,并在不需要时归还给池子而非直接销毁。对象池是一个特殊的工厂对象,对象池模式就是单例模式加享元模式。
享元模式(Flyweight Pattern)是一种结构型设计模式,它用于减少创建对象的数量,从而降低内存的使用。享元模式通过共享尽可能多的相似对象来实现这一点。
适用场景
对象池模式主要适用于以下应用场景。
- 资源受限的场景。比如,不需要可伸缩性的环境(CPU\内存等物理资源有限),CPU性能不够强劲,内存比较紧张,垃圾收集,内存抖动会造成比较大的影响,需要提高内存管理效率, 响应性比吞吐量更为重要。
- 在内存中数量受限的对象。
- 创建成本高的对象,可以考虑池化。
- 在需要快速响应的系统中,对象池可以减少等待对象创建的时间,从而提高系统的响应性。
常见的使用对象池的场景有在使用Socket时的各种连接池、线程池、数据库连接池等。
线程池也算特殊的对象池
实现方式
对象池的使用:
- 从对象池中获取对象,如果没有对象,则创建一个,并返回
- 使用对象
- 使用完成对象后,将对象还回对象池
实现对象池时注意的点:
- 对象的自动回收
- 线程安全
- 对象池重复利用时进行reset,保持对象的一致性
对象池的实现:
- 实现使用shared_ptr或者unique_ptr。
- 实现采取new和delete,需要自己去检查什么对象需要回收
无法直接将shared_ptr的删除器修改为自定义删除器,所以在使用shared_ptr时,需要一开始就定义回收函数,并把创建都委托给对象池
使用unique_ptr时,可以很方便的修改删除器
缺点和可以优化的点
对象池的缺点:
- 对象池在使用的时候,如果不加以控制,那么会增加内存消耗
比如仅在某个时间段突然需要大量对象,其他时间只用少量对象
此时对象池不够,需要添加更多的对象。而当增加完对象后,大量对象长时间存在内存里没有被使用,就导致了内存消耗。
未来优化:
- 可以在对象池初始化时传入工厂对象,用工厂来进行创建,而不是简单的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