iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++实现对象池的具体方法
  • 951
分享到

C++实现对象池的具体方法

2024-04-02 19:04:59 951人浏览 独家记忆
摘要

目录前言一、什么是对象池二、如何实现1.确定接口2.转成代码三、完整代码四、使用示例1、对象复用,示例:2、简易的线程池,示例:总结前言 需求无限,但资源有限的情况下,就需要对资源进

前言

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

一、什么是对象池

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

二、如何实现

1.确定接口

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

在这里插入图片描述

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

在这里插入图片描述

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();
	//使用结束,删除缓存
	delete	objectPool.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

总结

以上就是今天要讲的内容,本文介绍了对象池的设计与实现以及使用,其使用场景其实不算多,因为很多需要对象复用的场景通常以及有底层实现了,比如线程池数据库的连接池等,所以本文讲的内容只能适用于少数的场景,比如waveOut播放音频时是可以使用对象池实现 的。但总得来说,对象池还是有用的,所以将其写成博客用于记录曾经用过的技术。

到此这篇关于C++ 实现对象池的具体方法的文章就介绍到这了,更多相关C++ 对象池内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++实现对象池的具体方法

本文链接: https://www.lsjlt.com/news/162730.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++实现对象池的具体方法
    目录前言一、什么是对象池二、如何实现1.确定接口2.转成代码三、完整代码四、使用示例1、对象复用,示例:2、简易的线程池,示例:总结前言 需求无限,但资源有限的情况下,就需要对资源进...
    99+
    2024-04-02
  • C++如何实现对象池
    这篇“C++如何实现对象池”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“C++如何实现对象池”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们进入主题吧。前言需求...
    99+
    2023-06-26
  • Java对象池技术的原理及其实现方法
    这篇文章主要讲解了“Java对象池技术的原理及其实现方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java对象池技术的原理及其实现方法”吧!摘 要 :本文在分析对象池技术基本原理的基础上...
    99+
    2023-06-03
  • C++实现WPF动画的具体操作方法
    本篇文章为大家展示了C++实现WPF动画的具体操作方法,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。C++编程语言的应方式非常广泛,可以帮助我们轻松的实现许多功能需求。很多人都习惯使用Blend来帮...
    99+
    2023-06-17
  • C++内存池的实现方法
    这篇文章主要讲解了“C++内存池的实现方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++内存池的实现方法”吧!目录一、内存池基础知识什么是内存池1 池化技术2 内存池内存池的作用1 效...
    99+
    2023-06-20
  • c++ Bellman-Ford算法的具体实现
    Bellman-Ford算法用于解决有边数限制的最短路问题,且可以应对有负边权的图 其时间复杂度为O(nm),效率较低 代码实现: #include<iostream&g...
    99+
    2024-04-02
  • C++ 函数模板的语法及具体实现方法?
    c++++函数模板允许使用泛型类型参数定义函数,使函数可以处理不同类型的数据。具体实现如下:语法:template 返回类型 函数名(输入参数列表){ // 函数体 }泛型类型参数 t...
    99+
    2024-04-15
    c++ 函数模板
  • Java中对象池怎么实现
    这篇“Java中对象池怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中对象池怎么实现”文章吧。1. 什么是对...
    99+
    2023-07-02
  • GolangMutex实现互斥的具体方法
    目录获取锁未锁——直接获取在不饥饿且旋的不多的情况下,尝试自旋自旋究竟在做什么呢?计算期望状态尝试达成获取锁期望考虑几种场景释放锁只有已锁—&md...
    99+
    2023-05-17
    Golang Mutex互斥 Golang Mutex
  • C++类和对象实战之Date类的实现方法
    目录零、前言一、Date类相关接口二、具体接口函数实现1、获取月份天数2、Date打印3、Date构造函数4、Date析构函数5、Date拷贝构造函数6、Date赋值重载函数7、Da...
    99+
    2024-04-02
  • 深入理解C++中的new和delete并实现对象池
    深入理解new和delete new和delete称作运算符 我们转反汇编看看 这2个运算符本质也是相应的运算符的重载的调用 malloc和new的区别? 1.malloc按字节...
    99+
    2024-04-02
  • Mybatis实体类对象入参查询的方法
    本篇内容介绍了“Mybatis实体类对象入参查询的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mybatis实体类对象入参查询测试实体...
    99+
    2023-07-02
  • C/C++ 原生API实现线程池的方法
    线程池有两个核心的概念,一个是任务队列,一个是工作线程队列。任务队列负责存放主线程需要处理的任务,工作线程队列其实是一个死循环,负责从任务队列中取出和运行任务,可以看成是一个生产者和...
    99+
    2024-04-02
  • java实体对象转map的方法是什么
    Java实体对象转Map的方法有以下几种:1. 使用Java反射机制:通过使用Java的反射机制,可以获取实体对象的所有字段和对应的...
    99+
    2023-09-16
    java
  • c语言结构体字节对齐的实现方法
    目录1.什么是字节对齐 2.为什么要有字节对齐 3.手动设置对齐 4.结构体比较方法 1.什么是字节对齐 在c语言的结构体里面一般会按照某种规则去进行字节对齐。 我们先看一段代码...
    99+
    2024-04-02
  • 一文搞懂Java中对象池的实现
    目录1. 什么是对象池2. 为什么需要对象池3. 对象池的实现4. 开源的对象池工具5. JedisPool 对象池实现分析6. 对象池总结最近在分析一个应用中的某个接口的耗时情况时...
    99+
    2024-04-02
  • OpenCV实现对象跟踪的方法
    介绍 OpenCV 是一个很好的处理图像和视频的工具。无论你是想让你的照片呈现 90 年代的黑白效果,还是执行复杂的数学运算,OpenCV 都可以随时为你服务。 如果你对计算机视觉感...
    99+
    2024-04-02
  • golang对象池的实现原理是什么
    Golang对象池是一种用于重复利用对象的机制,以避免频繁的创建和销毁对象的开销。它通过预先创建一定数量的对象,并在需要时从池中获取...
    99+
    2023-10-27
    golang
  • c++线程池实现的方法是什么
    C++线程池的实现方法可以使用C++中的多线程库,如std::thread和std::mutex等来实现。以下是一个简单的C++线程...
    99+
    2023-10-26
    c++
  • Rust实现面向对象的方法
    目录前言1、实现封装(pub)2、实现继承(trait)2.1、为共有行为定义一个 Trait2.2、Trait 对象执行的是动态派发2.3、Trait对象必须保证对象安全3、实现多...
    99+
    2022-11-13
    Rust面向对象 Rust面向对象实现
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作