前言
一. 对象池是什么?
大家应该对线程池,内存池等即便不知道原理,也有所耳闻了。同样的,对象池的大概思路和线程池,内存池一样。
以线程池为例子说明,假定有一个服务端,一开始便分配了一定量的线程数(假设10个)。那么,当有10个客户端对服务端发起连接时。服务端便可直接将这10个客户端分配给事先准备的10个线程,而不需要临时创建10个线程,进而节省了大量的时间,同时也限制了一共可以处理的线程数,避免了资源过度占用。
对象池也是如此,**缓存一些对象从而避免大量创建同一个类型的对象,同时限制了实例的个数。**对象池在游戏中就有常用的地方,如地图刷新时,就直接从对象池中直接拿出所有的怪物,而并非逐个创建怪物。
二.对象池应用场景
1. 对象类型体积较大。
2. 需要频繁创建和销毁对象,对象生命周期短。
3. 资源有限,不能一次性分配过多对象,所以设置对象池来限制对象的数量,同时增加对象的复用。
三.对象池图解
如上图中, 对象池的大小 = 空闲队列(freePool)的大小 + 工作队列(busyPool)的大小。 同理的,当工作对象使用完毕后,我们只需要调用其析构函数来进行释放。然后将该对象push回freePool队列中。
四.对象池简单实现
/* objectPool.h */
#ifndef OBJECT_POOL_H_
#define OBJECT_POOL_H_
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <memory>
#include <queue>
#include <vector>
#include <mutex>
/* 定义对象池模板类 */
template<typename T>
class ObjectPool
{
public:
ObjectPool(size_t chuckSize = 10);
virtual ~ObjectPool();
void SetChuckSize(size_t chuckSize);
/* 分配Object,并获取 */
std::shared_ptr<T> AssignObject();
/* 释放对象,并回到空闲对象队列 */
void ReleaseObject(std::shared_ptr<T> obj);
private:
/* 构造函数中调用 */
void AllocateChunk();
/* 析构函数中调用 */
void DestroyPool();
private:
/* 空闲对象池 */
std::queue<std::shared_ptr<T> > _freeObjPool;
/* 整体对象的大池 */
std::queue<std::shared_ptr<T> > _allObjPool;
size_t _chuckSize;
std::mutex _queMutex;
};
template<typename T>
ObjectPool<T>::ObjectPool(size_t chuckSize):_chuckSize(chuckSize){
if(_chuckSize < 0){
assert(false);
return;
}
for (size_t i = 0; i < _chuckSize; i++)
{
/* 创建新对象,并进入空闲对象队列 */
AllocateChunk();
}
}
template<typename T>
ObjectPool<T>::~ObjectPool(){
DestroyPool();
}
template<typename T>
void ObjectPool<T>::SetChuckSize(size_t chuckSize){
if(chuckSize > _chuckSize){
for (size_t i = 0; i < chuckSize - _chuckSize; i++)
{
AllocateChunk();
}
}else
{
return;
}
}
template<typename T>
void ObjectPool<T>::AllocateChunk(){
std::shared_ptr<T> Obj(new T());
_queMutex.lock();
_freeObjPool.push(Obj);
_allObjPool.push(Obj);
_queMutex.unlock();
}
template<typename T>
std::shared_ptr<T> ObjectPool<T>::AssignObject(){
/* 若空闲队列为空,则另外分配一个对象 */
if(_freeObjPool.empty()){
AllocateChunk();
}
if(_freeObjPool.empty()){
assert(false);
return nullptr;
}
/* 从空闲队列的头部取出一个obj */
std::shared_ptr<T> assignObj = _freeObjPool.front();
/* 出队 */
_queMutex.lock();
_freeObjPool.pop();
_queMutex.unlock();
return assignObj;
}
template<typename T>
void ObjectPool<T>::ReleaseObject(std::shared_ptr<T> Obj){
if(Obj == nullptr){
assert(false);
return;
}
Obj->~T();
/* 加入回空闲对象队列 */
_queMutex.lock();
_freeObjPool.push(Obj);
_queMutex.unlock();
}
template<typename T>
void ObjectPool<T>::DestroyPool(){
if(_allObjPool.empty()){
assert(false);
return;
}
_queMutex.lock();
for (size_t i = 0; i < _chuckSize; i++)
{
std::shared_ptr<T> tmpObj = _allObjPool.front();
_allObjPool.pop();
}
_queMutex.unlock();
}
#endif
/* main.cpp */
#include "objectPool.hpp"
class Test
{
public:
void PrintAddr()
{
printf( "address: %p\n", this );
}
};
int main()
{
/* 初始化Test类型的对象池,size为2 */
ObjectPool< Test > TestPool( 2 );
/* 从对象池中分配obj */
std::shared_ptr<Test> pTest1 = TestPool.AssignObject();
std::shared_ptr<Test> pTest2 = TestPool.AssignObject();
pTest1->PrintAddr();
pTest2->PrintAddr();
/* 回到对象池中 */
TestPool.ReleaseObject( pTest1 );
TestPool.ReleaseObject( pTest2 );
/* 从对象池中分配obj */
pTest1 = TestPool.AssignObject();
pTest2 = TestPool.AssignObject();
pTest1->PrintAddr();
pTest2->PrintAddr();
/* 回到对象池中 */
TestPool.ReleaseObject( pTest1 );
TestPool.ReleaseObject( pTest2 );
return 0;
}
命令行中执行如下命令编译:
g++ main.cpp -o test -std=c++11
./test
我们可以看到对象池建立成功了,从打印出来的pTest1和pTest2的地址看得出对象是多次复用的,迟点将队列换成无锁队列。