文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++如何实现对象池

2023-06-26 06:51

关注

这篇“C++如何实现对象池”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“C++如何实现对象池”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们进入主题吧。

前言

需求无限,但资源有限的情况下,就需要对资源进行专门的管理。不断的申请和释放内存是不合理的,会造成内存的波动,以及内存不受限的增长。比如,实现了一个消息队列,当发消息的速度快于处理消息的速度时,如果不对资源进行控制,就会导致内存不断的增长。除非有专门的内存管理机制,或明确的编译器优化内存复用,否则建立一个资源管理模块是很有必要的。对象池就是一个对限定数量资源复用管理的模块。

一、什么是对象池

复用对象,消除频繁的对象创建销毁带来的性能消耗,以及避免内存增长的不可控。比如,线程池、连接池都是为了实现复用对象。
举个例子:假设在生产者消费者模型中,生产者生产时创建对象,消费者消费后销毁对象。直接简单的使用new和delete,就会让对象频繁创建和销毁导致额外性能消耗,而且生产者速度大于消费者速度时,就会让对象数量创建大于销毁导致内存不受控制增长。如果使用对象池,就可以让生产和消费复用固定数量的对象,很好的避免了频繁创建销毁对象以及内存增长不受控制的情况。

二、如何实现

1.确定接口

(1)、确定动态关系
通过序列图可以确定对象需要的接口,我们以socket服务为场景绘制序列图,如下

C++如何实现对象池

(2)、确定静态关系
根据上面的序列图确定的接口绘制成类图,如下:

C++如何实现对象池

2.转成代码

由于模块规模小,接口也不多,所以就不展示进一步细化设计了。因为本文讲述的是C++实现对象池,所以将上述设计转化为C++接口定义。如下:

    /// <summary>/// 对象池/// </summary>class ObjectPool{public:/// <summary>/// 构造方法/// </summary>/// <param name="bufferArray">对象池的缓冲区,由外部指定,可以理解为一个数组。数组大小需满足bufferSize>=elementSize*arraySize</param>/// <param name="elementSize">数组元素大小</param>/// <param name="arraySize">数组长度或元素个数</param>ObjectPool(void* bufferArray, int elementSize, int arraySize );/// <summary>/// 申请对象/// 如果池里对象不足,则会等待,直到有对象才返回。/// </summary>/// <returns>返回申请的对象指针</returns>void* Applicate();/// <summary>/// 申请对象/// </summary>/// <param name="timeout">超时时间,超时后返回null</param>/// <returns>返回申请的对象指针</returns>void* Applicate(int timeout);/// <summary>/// 归还对象/// </summary>/// <param name="element">需归还的对象</param>void ReturnBack(void* element);};

三、完整代码

根据上述的初步设计,再进行细化,以及实现,最终得出如下代码实现。
ObjectPool.h

#ifndef OBJECTPOOL_H#define OBJECTPOOL_H#include<unordered_map>#include<vector>#include<mutex>#include<condition_variable>namespace AC {/// <summary>/// 对象池/// </summary>class ObjectPool{public:/// <summary>/// 构造方法/// </summary>/// <param name="bufferArray">对象池的缓冲区,由外部指定,可以理解为一个数组。数组大小需满足bufferSize>=elementSize*arraySize</param>/// <param name="elementSize">数组元素大小</param>/// <param name="arraySize">数组长度或元素个数</param>ObjectPool(void* bufferArray, int elementSize, int arraySize );/// <summary>/// 析构方法/// </summary>~ObjectPool();/// <summary>/// 申请对象/// 如果池里对象不足,则会等待,直到有对象才返回。/// </summary>/// <returns>返回申请的对象指针</returns>void* Applicate();/// <summary>/// 申请对象/// </summary>/// <param name="timeout">超时时间,超时后返回null</param>/// <returns>返回申请的对象指针</returns>void* Applicate(int timeout);/// <summary>/// 归还对象/// </summary>/// <param name="element">需归还的对象</param>void ReturnBack(void* element);/// <summary>/// 获取对象池的缓冲区,即构造方法中的bufferArray/// </summary>/// <returns>缓冲区的指针</returns>void* GetPoolBuffer();/// <summary>/// 获取对象的大小,即构造方法中的elementSize/// </summary>/// <returns>对象的大小</returns>int GetObjectSize();/// <summary>/// 获取总的对象数量,即构造方法中的arraySize/// </summary>/// <returns>总的对象数量</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>/// 泛型对象池/// </summary>/// <typeparam name="T">对象类型</typeparam>template<typename T>class ObjectPoolGeneric:private ObjectPool{public:/// <summary>/// 构造方法/// </summary>/// <param name="array">对象数组</param>/// <param name="size">数组大小</param>/// <returns></returns>ObjectPoolGeneric(T*array,int size) :ObjectPool(array, sizeof(T), size){}/// <summary>/// 析构方法/// </summary>~ObjectPoolGeneric() {}/// <summary>/// 申请对象/// 如果池里对象不足,则会等待,直到有对象才返回。/// </summary>/// <returns>返回申请的对象指针</returns>T* Applicate() {return (T*)ObjectPool::Applicate();}/// <summary>/// 申请对象/// </summary>/// <param name="timeout">超时时间,超时后返回null</param>/// <returns>返回申请的对象指针</returns>T* Applicate(int timeout) {return (T*)ObjectPool::Applicate(timeout);}/// <summary>/// 归还对象/// </summary>/// <param name="element">需归还的对象</param>void ReturnBack(T* element){ObjectPool::ReturnBack(element);}/// <summary>/// 获取对象池的缓冲区,即构造方法中的bufferArray/// </summary>/// <returns>缓冲区的指针</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、对象复用,示例:

#include "ObjectPool.h"class A {public:A() {printf("%p\n", this);}};int main(int argc, char** argv) {//初始化对象池,2个对象AC::ObjectPool objectPool(new char[sizeof(A) * 2], sizeof(A), 2);A* a, * b, * c;//申请对象,使用定位new初始化对象a = new (objectPool.Applicate())A;b = new (objectPool.Applicate())A;//归还对象a->~A();//返初始化对象objectPool.ReturnBack(a);c = new (objectPool.Applicate())A;b->~A();c->~A();//使用结束,删除缓存deleteobjectPool.GetPoolBuffer();return 0;}

输出:
016502E9
016502E8
016502E9

2、简易的线程池,示例:

#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;};//线程信息数组,数组长度即线程池的线程数ThreadInfo _threadArray[3];//对象池,使用线程信息数组初始化AC::ObjectPoolGeneric<ThreadInfo>_threadPool(_threadArray, 3);bool _exitThreadPool = false;//在线程池中运行方法void RunInThreadPool(std::function<void()> f) {//申请线程auto threadInfo = _threadPool.Applicate();threadInfo->_threadPoc = f; if (threadInfo->pThread)//复用线程{threadInfo->_cv.notify_one();}else//创建线程{threadInfo->pThread = new std::thread([=]() {while (!_exitThreadPool){printf("thread %d run\n", threadInfo->pThread->get_id());if (threadInfo->_threadPoc){//执行线程操作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){   //在线程池中运行方法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
&hellip;

以上是“C++如何实现对象池”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯