C++ 實(shí)現(xiàn)對(duì)象池的具體方法
前言
需求無(wú)限,但資源有限的情況下,就需要對(duì)資源進(jìn)行專門(mén)的管理。不斷的申請(qǐng)和釋放內(nèi)存是不合理的,會(huì)造成內(nèi)存的波動(dòng),以及內(nèi)存不受限的增長(zhǎng)。比如,實(shí)現(xiàn)了一個(gè)消息隊(duì)列,當(dāng)發(fā)消息的速度快于處理消息的速度時(shí),如果不對(duì)資源進(jìn)行控制,就會(huì)導(dǎo)致內(nèi)存不斷的增長(zhǎng)。除非有專門(mén)的內(nèi)存管理機(jī)制,或明確的編譯器優(yōu)化內(nèi)存復(fù)用,否則建立一個(gè)資源管理模塊是很有必要的。對(duì)象池就是一個(gè)對(duì)限定數(shù)量資源復(fù)用管理的模塊。
一、什么是對(duì)象池
復(fù)用對(duì)象,消除頻繁的對(duì)象創(chuàng)建銷(xiāo)毀帶來(lái)的性能消耗,以及避免內(nèi)存增長(zhǎng)的不可控。比如,線程池、連接池都是為了實(shí)現(xiàn)復(fù)用對(duì)象。
舉個(gè)例子:假設(shè)在生產(chǎn)者消費(fèi)者模型中,生產(chǎn)者生產(chǎn)時(shí)創(chuàng)建對(duì)象,消費(fèi)者消費(fèi)后銷(xiāo)毀對(duì)象。直接簡(jiǎn)單的使用new和delete,就會(huì)讓對(duì)象頻繁創(chuàng)建和銷(xiāo)毀導(dǎo)致額外性能消耗,而且生產(chǎn)者速度大于消費(fèi)者速度時(shí),就會(huì)讓對(duì)象數(shù)量創(chuàng)建大于銷(xiāo)毀導(dǎo)致內(nèi)存不受控制增長(zhǎng)。如果使用對(duì)象池,就可以讓生產(chǎn)和消費(fèi)復(fù)用固定數(shù)量的對(duì)象,很好的避免了頻繁創(chuàng)建銷(xiāo)毀對(duì)象以及內(nèi)存增長(zhǎng)不受控制的情況。
二、如何實(shí)現(xiàn)
1.確定接口
(1)、確定動(dòng)態(tài)關(guān)系
通過(guò)序列圖可以確定對(duì)象需要的接口,我們以socket服務(wù)為場(chǎng)景繪制序列圖,如下

(2)、確定靜態(tài)關(guān)系
根據(jù)上面的序列圖確定的接口繪制成類圖,如下:

2.轉(zhuǎn)成代碼
由于模塊規(guī)模小,接口也不多,所以就不展示進(jìn)一步細(xì)化設(shè)計(jì)了。因?yàn)楸疚闹v述的是C++實(shí)現(xiàn)對(duì)象池,所以將上述設(shè)計(jì)轉(zhuǎn)化為C++接口定義。如下:
/// <summary>
/// 對(duì)象池
/// </summary>
class ObjectPool
{
public:
/// <summary>
/// 構(gòu)造方法
/// </summary>
/// <param name="bufferArray">對(duì)象池的緩沖區(qū),由外部指定,可以理解為一個(gè)數(shù)組。數(shù)組大小需滿足bufferSize>=elementSize*arraySize</param>
/// <param name="elementSize">數(shù)組元素大小</param>
/// <param name="arraySize">數(shù)組長(zhǎng)度或元素個(gè)數(shù)</param>
ObjectPool(void* bufferArray, int elementSize, int arraySize );
/// <summary>
/// 申請(qǐng)對(duì)象
/// 如果池里對(duì)象不足,則會(huì)等待,直到有對(duì)象才返回。
/// </summary>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
void* Applicate();
/// <summary>
/// 申請(qǐng)對(duì)象
/// </summary>
/// <param name="timeout">超時(shí)時(shí)間,超時(shí)后返回null</param>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
void* Applicate(int timeout);
/// <summary>
/// 歸還對(duì)象
/// </summary>
/// <param name="element">需歸還的對(duì)象</param>
void ReturnBack(void* element);
};
三、完整代碼
根據(jù)上述的初步設(shè)計(jì),再進(jìn)行細(xì)化,以及實(shí)現(xiàn),最終得出如下代碼實(shí)現(xiàn)。
ObjectPool.h
#ifndef OBJECTPOOL_H
#define OBJECTPOOL_H
/************************************************************************
* @Project: AC::ObjectPool
* @Decription: 對(duì)象池:“需求很大,但數(shù)量有限”的情況下,就需要對(duì)資源進(jìn)行專門(mén)的管理,
*不斷的申請(qǐng)和釋放對(duì)象是不合理的(除非有專門(mén)的內(nèi)存管理機(jī)制,或明確的編譯優(yōu)化內(nèi)存復(fù)用)。
*這是一個(gè)對(duì)限定數(shù)量資源的復(fù)用管理模塊。
* @Verision: v1.0.0.1
* @Author: Xin Nie
* @Create: 2018/12/21 13:34:00
* @LastUpdate: 2022/1/5 13:53:00
************************************************************************
* Copyright @ 2022. All rights reserved.
************************************************************************/
#include<unordered_map>
#include<vector>
#include<mutex>
#include<condition_variable>
namespace AC {
/// <summary>
/// 對(duì)象池
/// </summary>
class ObjectPool
{
public:
/// <summary>
/// 構(gòu)造方法
/// </summary>
/// <param name="bufferArray">對(duì)象池的緩沖區(qū),由外部指定,可以理解為一個(gè)數(shù)組。數(shù)組大小需滿足bufferSize>=elementSize*arraySize</param>
/// <param name="elementSize">數(shù)組元素大小</param>
/// <param name="arraySize">數(shù)組長(zhǎng)度或元素個(gè)數(shù)</param>
ObjectPool(void* bufferArray, int elementSize, int arraySize );
/// <summary>
/// 析構(gòu)方法
/// </summary>
~ObjectPool();
/// <summary>
/// 申請(qǐng)對(duì)象
/// 如果池里對(duì)象不足,則會(huì)等待,直到有對(duì)象才返回。
/// </summary>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
void* Applicate();
/// <summary>
/// 申請(qǐng)對(duì)象
/// </summary>
/// <param name="timeout">超時(shí)時(shí)間,超時(shí)后返回null</param>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
void* Applicate(int timeout);
/// <summary>
/// 歸還對(duì)象
/// </summary>
/// <param name="element">需歸還的對(duì)象</param>
void ReturnBack(void* element);
/// <summary>
/// 獲取對(duì)象池的緩沖區(qū),即構(gòu)造方法中的bufferArray
/// </summary>
/// <returns>緩沖區(qū)的指針</returns>
void* GetPoolBuffer();
/// <summary>
/// 獲取對(duì)象的大小,即構(gòu)造方法中的elementSize
/// </summary>
/// <returns>對(duì)象的大小</returns>
int GetObjectSize();
/// <summary>
/// 獲取總的對(duì)象數(shù)量,即構(gòu)造方法中的arraySize
/// </summary>
/// <returns>總的對(duì)象數(shù)量</returns>
int GetObjectCount();
private:
void*_buffer = NULL;
int _elementSize;
int _arraySize;
std::vector<void*> _unusedUnits;
std::unordered_map<void*, int> _usedUnits;
std::mutex _mutex;
std::condition_variable _cond;
};
/// <summary>
/// 泛型對(duì)象池
/// </summary>
/// <typeparam name="T">對(duì)象類型</typeparam>
template<typename T>
class ObjectPoolGeneric:private ObjectPool
{
public:
/// <summary>
/// 構(gòu)造方法
/// </summary>
/// <param name="array">對(duì)象數(shù)組</param>
/// <param name="size">數(shù)組大小</param>
/// <returns></returns>
ObjectPoolGeneric(T*array,int size) :ObjectPool(array, sizeof(T), size)
{
}
/// <summary>
/// 析構(gòu)方法
/// </summary>
~ObjectPoolGeneric() {}
/// <summary>
/// 申請(qǐng)對(duì)象
/// 如果池里對(duì)象不足,則會(huì)等待,直到有對(duì)象才返回。
/// </summary>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
T* Applicate() {
return (T*)ObjectPool::Applicate();
}
/// <summary>
/// 申請(qǐng)對(duì)象
/// </summary>
/// <param name="timeout">超時(shí)時(shí)間,超時(shí)后返回null</param>
/// <returns>返回申請(qǐng)的對(duì)象指針</returns>
T* Applicate(int timeout) {
return (T*)ObjectPool::Applicate(timeout);
}
/// <summary>
/// 歸還對(duì)象
/// </summary>
/// <param name="element">需歸還的對(duì)象</param>
void ReturnBack(T* element)
{
ObjectPool::ReturnBack(element);
}
/// <summary>
/// 獲取對(duì)象池的緩沖區(qū),即構(gòu)造方法中的bufferArray
/// </summary>
/// <returns>緩沖區(qū)的指針</returns>
T* GetPoolBuffer() {
return (T*)ObjectPool::GetPoolBuffer();
}
};
}
#endif
ObjectPool.cpp
#include "ObjectPool.h"
#include <chrono>
namespace AC {
ObjectPool::ObjectPool(void* bufferArray, int elementSize, int arraySize)
{
if (elementSize < 1 || arraySize < 1)
return;
_buffer = bufferArray;
_elementSize = elementSize;
_arraySize = arraySize;
char* firstAdress = (char*)bufferArray;
//記錄未使用的索引
for (int i = 0; i < arraySize; i++)
{
_unusedUnits.push_back(&(firstAdress[i * elementSize]));
}
}
ObjectPool::~ObjectPool()
{
}
void* ObjectPool::Applicate()
{
return Applicate(-1);
}
void* ObjectPool::Applicate(int timeout) {
void* resource = NULL;
std::unique_lock<std::mutex> l(_mutex);
while (_unusedUnits.size() < 1)
{
if (timeout == -1)
{
_cond.wait(l);
}
else if (_cond.wait_for(l, std::chrono::microseconds(timeout)) == std::cv_status::timeout)
{
return nullptr;
}
}
resource = _unusedUnits.back();
_usedUnits[resource] = 1;
_unusedUnits.pop_back();
return resource;
}
void ObjectPool::ReturnBack(void* element) {
_mutex.lock();
auto iter = _usedUnits.find(element);
if (iter != _usedUnits.end())
{
_unusedUnits.push_back(element);
_usedUnits.erase(iter);
_cond.notify_one();
}
_mutex.unlock();
}
void* ObjectPool::GetPoolBuffer()
{
return _buffer;
}
int ObjectPool::GetObjectSize()
{
return _elementSize;
}
int ObjectPool::GetObjectCount()
{
return _arraySize;
}
}
四、使用示例
1、對(duì)象復(fù)用,示例:
#include "ObjectPool.h"
class A {
public:
A() {
printf("%p\n", this);
}
};
int main(int argc, char** argv) {
//初始化對(duì)象池,2個(gè)對(duì)象
AC::ObjectPool objectPool(new char[sizeof(A) * 2], sizeof(A), 2);
A* a, * b, * c;
//申請(qǐng)對(duì)象,使用定位new初始化對(duì)象
a = new (objectPool.Applicate())A;
b = new (objectPool.Applicate())A;
//歸還對(duì)象
a->~A();//返初始化對(duì)象
objectPool.ReturnBack(a);
c = new (objectPool.Applicate())A;
b->~A();
c->~A();
//使用結(jié)束,刪除緩存
delete objectPool.GetPoolBuffer();
return 0;
}
輸出:
016502E9
016502E8
016502E9
2、簡(jiǎn)易的線程池,示例:
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include "ObjectPool.h"
class ThreadInfo {
public:
std::thread* pThread;
std::mutex _mutext;
std::condition_variable _cv;
std::function<void()> _threadPoc;
};
//線程信息數(shù)組,數(shù)組長(zhǎng)度即線程池的線程數(shù)
ThreadInfo _threadArray[3];
//對(duì)象池,使用線程信息數(shù)組初始化
AC::ObjectPoolGeneric<ThreadInfo>_threadPool(_threadArray, 3);
bool _exitThreadPool = false;
//在線程池中運(yùn)行方法
void RunInThreadPool(std::function<void()> f) {
//申請(qǐng)線程
auto threadInfo = _threadPool.Applicate();
threadInfo->_threadPoc = f;
if (threadInfo->pThread)
//復(fù)用線程
{
threadInfo->_cv.notify_one();
}
else
//創(chuàng)建線程
{
threadInfo->pThread = new std::thread([=]() {
while (!_exitThreadPool)
{
printf("thread %d run\n", threadInfo->pThread->get_id());
if (threadInfo->_threadPoc)
{ //執(zhí)行線程操作
threadInfo->_threadPoc();
}
printf("thread %d stop\n", threadInfo->pThread->get_id());
//歸還線程
_threadPool.ReturnBack(threadInfo);
std::unique_lock<std::mutex>lck(threadInfo->_mutext);
threadInfo->_cv.wait(lck);
}
});
}
}
int main(int argc, char** argv) {
while(true)
{ //在線程池中運(yùn)行方法
RunInThreadPool([]() {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
});
}
return 0;
}
輸出:
thread 69664 run
thread 57540 run
thread 56876 run
thread 69664 stop
thread 69664 run
thread 57540 stop
thread 56876 stop
thread 57540 run
thread 56876 run
thread 69664 stop
thread 69664 run
thread 56876 stop
thread 57540 stop
thread 56876 run
thread 57540 run
thread 69664 stop
…
總結(jié)
以上就是今天要講的內(nèi)容,本文介紹了對(duì)象池的設(shè)計(jì)與實(shí)現(xiàn)以及使用,其使用場(chǎng)景其實(shí)不算多,因?yàn)楹芏嘈枰獙?duì)象復(fù)用的場(chǎng)景通常以及有底層實(shí)現(xiàn)了,比如線程池?cái)?shù)據(jù)庫(kù)的連接池等,所以本文講的內(nèi)容只能適用于少數(shù)的場(chǎng)景,比如waveOut播放音頻時(shí)是可以使用對(duì)象池實(shí)現(xiàn) 的。但總得來(lái)說(shuō),對(duì)象池還是有用的,所以將其寫(xiě)成博客用于記錄曾經(jīng)用過(guò)的技術(shù)。
到此這篇關(guān)于C++ 實(shí)現(xiàn)對(duì)象池的具體方法的文章就介紹到這了,更多相關(guān)C++ 對(duì)象池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++11 chrono全面解析(最高可達(dá)納秒級(jí)別的精度)
chrono是c++ 11中的時(shí)間庫(kù),本文就來(lái)詳細(xì)的介紹一下chrono庫(kù)的具體使用,關(guān)鍵是理解里面時(shí)間段(Durations)、時(shí)間點(diǎn)(Time points)的概念,感興趣的可以了解一下2021-11-11
c語(yǔ)言實(shí)現(xiàn)含遞歸清場(chǎng)版掃雷游戲
掃雷大家應(yīng)該都玩過(guò),這是一個(gè)十分經(jīng)典的游戲,下面這篇文章主要給大家介紹了關(guān)于c語(yǔ)言實(shí)現(xiàn)含遞歸清場(chǎng)版掃雷游戲的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-11-11
VC創(chuàng)建DLL動(dòng)態(tài)鏈接庫(kù)的方法
這篇文章主要介紹了VC創(chuàng)建DLL動(dòng)態(tài)鏈接庫(kù)的方法,實(shí)例分析VC創(chuàng)建動(dòng)態(tài)鏈接庫(kù)的完整步驟,需要的朋友可以參考下2015-05-05
QT使用Http協(xié)議通信的實(shí)現(xiàn)示例
使用QT進(jìn)行應(yīng)用開(kāi)發(fā)時(shí),有時(shí)候需要進(jìn)行客戶端和服務(wù)端的網(wǎng)絡(luò)通信,本文主要介紹了QT使用Http協(xié)議通信的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
基于c++的中國(guó)象棋游戲設(shè)計(jì)與實(shí)現(xiàn)
這篇文章主要介紹了基于c++的中國(guó)象棋游戲設(shè)計(jì)與實(shí)現(xiàn),主要操作是possibleMove(int?x,?int?y),通過(guò)整個(gè)棋盤(pán)每個(gè)位置上的信息、中國(guó)象棋的規(guī)則來(lái)獲得位置(x,?y)這個(gè)棋子可以移動(dòng)到的位置,需要的朋友可以參考一下2022-02-02

