一、内存池原理
1、我们先用生活中的例子来解释什么是内存池:
(1)每个月月底钱花完时,或者急需要用钱时,你就打电话给你父母要钱,然后父母把钱通过微信或支付宝转给你。这种方式,每次要用钱时就需要联系你父母,然后你父母再把钱给你,4年大学下来,这里面耗费的时间也精力肯定不少。
(2)父母一次性给你一大笔钱,你随便用随便花。这样肯定方便多了,这就类似内存池方式。
所以内存池的思想,就是在真正使用内存之前,预先申请分配一定数量、大小预设的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,当内存释放后就回归到内存池留作后续的复用。使得内存使用效率得到提升,一般也不会产生不可控制的内存碎片。
2、那么在C++中怎么实现具体的线程池呢?简单地说,需要下面这几个步骤:
(1)先申请一块连续的内存空间。
(2)内存节点:含1个数据对象和指向下一个数据对象的指针。每个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间。
(3)某个内存节点一旦分配出去,从空闲内存节点链表中移除。
(4)某个内存节点一旦被释放,将该内存节点重新加入空闲内存节点链表。
(5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。
二、用C++实现内存池
1、定义一个内存节点
template<typename T>
struct Node{
T data; // 元素
Node<T>* next; // 指向下一个节点的指针
};
2、初始化1个内存块
//当前没有空闲节点
if(this->pMemoryHead == nullptr){
this->pMemoryHead = new Node<T>; //生成新节点
Node<T> *pTemp = this->pMemoryHead; //将数据连接起来
for(int i = 1; i < blockSize; i++){
pTemp->next = new Node<T>;
pTemp = pTemp->next;
}
pTemp->next = nullptr;
this->headCount += blockSize; //可用内存节点数
}
3、申请1个对象空间
Node<T> *pTemp = this->pMemoryHead;
pMemoryHead = pMemoryHead->next; //指向下一个未使用的节点
//因为temp指向的是当前将被使用的节点, 把它的next指向前面的被使用节点
pTemp->next = pUsedHead;
pUsedHead = pTemp; //指针指向最新将被使用的节点
this->headCount--;
this->usedCount++;
4、释放对象空间
Node<T> *pFree = (_Node<T>*)p; //释放的节点
Node<T> *pTemp = pUsedHead;
//第一种情况
if(pTemp == pFree){ //第一个就是
pUsedHead = pUsedHead->next;
pTemp->next = pMemoryHead; //将节点重新接到MemoryHead
pMemoryHead = pTemp;
this->usedCount--;
this->headCount++;
return;
}
//第二种情况
Node<T> *prev;
while(pTemp != nullptr){
if(pTemp == pFree){
prev->next = pTemp->next;
pTemp->next = MemoryHead;
MemoryHead = pTemp;
this->usedCount--;
this->headCount++;
return;
}
//前一个节点
prev = pTemp;
//查询下一个节点
pTemp = pTemp->next;
}
三、完整代码
1、MemoryPool.h
#include <iostream>
#include <list>
#include <string>
using namespace std;
template<typename T>
struct Node{ //节点
T data; //元素
Node<T>* next; //指向下一个节点的指针
};
template <typename T, int blockSize = 10>
class MemoryPool{
public:
MemoryPool();
~MemoryPool();
void* allocate();
int free(void *p);
int headCount;
int usedCount;
private:
Node<T>* pMemoryHead; //未使用的节点
Node<T>* pUsedHead; //正在使用的节点
};
2、MemoryPool.cpp
#include"MemoryPool.h"
#include<vector>
template <class T, int blockSize>
MemoryPool<T, blockSize>::MemoryPool(){
this->pMemoryHead = nullptr;
this->pUsedHead = nullptr;
this->headCount = 0;
this->usedCount = 0;
}
template <class T, int BlockSize>
MemoryPool<T, blockSize>::~MemoryPool(){
Node<T>* ptr;
while(this->pMemoryHead){
ptr = this->pMemoryHead->next;
delete this->pMemoryHead;
pMemoryHead = ptr;
}
while (this->pUsedHead){
ptr = this->pUsedHead->next;
delete this->pUsedHead;
pUsedHead = ptr;
}
}
template <class T, int blockSize>
void* MemoryPool<T, blockSize>::allocate(){
if(this->pMemoryHead == nullptr){
//当没有空闲节点
this->pMemoryHead = new Node<T>;
Node<T> *pTemp = this->pMemoryHead; //将数据连接起来
for(int i = 1; i < blockSize; i++){
pTemp->next = new Node<T>;
pTemp = pTemp->next;
}
pTemp->next = nullptr;
this->headCount += blockSize;
}
Node<T> *pTemp = pMemoryHead;
pMemoryHead = pMemoryHead->next; //指向下一个数据节点
//将data 挂在到UseHead
pTemp->next = pUsedHead;
pUsedHead = pTemp;
this->headCount--;
this->usedCount++;
return reinterpret_cast<void*>(pTemp);
}
template <typename T, int blockSize>
int MemoryPool<T, blockSize>::free(void *p){
//遍历 UsedHead链表
Node<T> *pFind = (Node<T>*)p;
Node<T> *pTemp = pUsedHead;
if(pTemp == pFind){ //第一个就是
pUsedHead = pUseHead->next;
pTemp->next = pMemoryHead; //将节点重新接到MemoryHead
pMemoryHead = pTemp;
this->usedCount--;
this->headCount++;
return 1;
}
Node<T> *prev;
while(pTemp != nullptr){
if(pTemp == pFind){
prev->next = pTemp->next;
pTemp->next = pMemoryHead;
pMemoryHead = pTemp;
this->usedCount--;
this->headCount++;
return 1;
}
prev = pTemp;
// 查下一个节点
pTemp = pTemp->next;
}
return -1;
}
int main(){
MemoryPool<int, 100> pa;
vector<int*> ret;
cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;
for(int i=0;i<50;i++){
ret.push_back(reinterpret_cast<int*>(pa.allocate()));
}
cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;
for(int i=0;i<10;i++){
pa.free(reinterpret_cast<void*>(ret[i]));
}
cout<<"head:"<<pa.headCount<<"use:"<<pa.usedCount<<endl;
return 0;
}