发布一个多线程安全的内存池

根据肖舸的《0 bug C++商用编程之道》中的内存池做的一些修改。在此感谢作者肖舸。

关于其中读写锁部分,各位可参照肖舸的读写锁,或者自行替换,毕竟这东西只有写没有读。单写多读锁意义不大。其实完全比不上最简单读写锁.

主要改进1.去掉递归查找分配块大小部分。改用函数直接计算出到需要的块大小

改进2.使用双向链表取代固定大小的数组。

改进3.释放和没释放的内存块均在双向链表中保存。方便调试输出

欢迎各位读者批评指正.

******************************************************************************************************************

头文件如下

#ifndef _MemPool_H_

#define _MemPool_H_
#include "MT_sync.h"
struct MemBlock
{
//ULONG m_ulDataSize; //请求分配的内存大小.(不包括头)
void* m_pData; //用户数据指针.
unsigned char m_nSizeIndex;//块大小索引
const char* m_strFileName;//文件名
long m_nLineNumber; //行号
MemBlock* m_pNext; //指向下一链表元素的指针
MemBlock* m_pFront; //指向上一个链表元素的指针
MemBlock()
{
//m_ulDataSize = 0; //请求分配的内存大小.(不包括头)
m_pData = 0; //用户数据指针.
m_nSizeIndex = 0;//块大小索引
m_pNext = 0; //指向下一链表元素的指针
m_pFront =0;//指向上一个链表元素的指针
}
void Print();
};
//用于统计内存块的信息
struct MemBlockInfo
{


//unsigned int m_nTotalMallocSize;//已分配块总大小
//unsigned int m_nTotalMallocCnt;//已分配块(指针)个数


//unsigned int m_nTotalFreeSize;//空闲内存块总大小
//unsigned int m_nTotalFreeCnt;//空闲块(指针)个数


long long m_nCurMallocSize;
unsigned int m_nCurMallocCnt;
unsigned int m_nBlockCnt;//首次分配的个数(即通过系统分配,且被管理的block的个数)
unsigned int m_nSysMallocCnt;


unsigned int m_nMaxPoint;//最大指针


MemBlockInfo()
{
//m_nFreeSize=0;//空闲内存块总大小
//m_nFreeCnt=0;//空闲块(指针)个数


m_nCurMallocSize =0;//已分配块总大小
m_nCurMallocCnt=0;//已分配块(指针)个数
m_nBlockCnt =0;
m_nSysMallocCnt=0;
}


void OnSysMalloc()
{
++m_nSysMallocCnt;
}


void OnSysFree()
{
--m_nSysMallocCnt;
}




void OnMalloc(int nSize,bool bFirst = false)
{
m_nCurMallocSize+=nSize;
++m_nCurMallocCnt;

if(bFirst)
{
++m_nBlockCnt;
}
}


void OnFree(int nSize)
{
m_nCurMallocSize-=nSize;
--m_nCurMallocCnt;
}




};
const int MemBlock_Size = sizeof(MemBlock);
const int Max_MemBlock_Array = 17;


class MemPool
{


public:
MemPool();
~MemPool();
public:
void* Malloc(unsigned int nSize,const char* file = NULL,long line = 0);
//释放一个块
void Free(void* pBlock);
//显示整棵内存树的内容
void PrintTree(void);
//关键信息显示
void PrintInfo(void);
public:


int GetSizeIndex(int nSize);//根据块大小,取得索引


private:


MemBlock* m_aryMallocBlock[Max_MemBlock_Array];//已分配的内存块数组(保存的是链表头)
MemBlock* m_aryFreeBlock[Max_MemBlock_Array];//空闲的内存块数组(保存的是链表头)
int m_aryMallocSize[Max_MemBlock_Array];
MemBlockInfo m_mallocInfo;
MRSWLock m_Lock;
};




extern MemPool& GetMemPool();




将一根指针注册到内存池,内存池默认在聚合工具类中
//#define MemPool_Malloc(TYPE,file,line ) \
// (TYPE*)(GetMemPool().Malloc(sizeof(TYPE),file,line))
//
//将一根指针注册到内存池,内存池默认在聚合工具类中
#define MemPool_Malloc(size,file,line ) \
GetMemPool().Malloc(size,file,line)




将一根指针注册到内存池,内存池默认在聚合工具类中
//#define MemPool_Malloc(size) \
// GetMemPool().Malloc(size)




//将一根指针从内存池反注册,内存池默认在聚合工具类中
#define MemPool_Free(pPoint) \
GetMemPool().Free(pPoint);
//
//将一根对象指针先执行反注册,再摧毁,最后置空
#define MemPool_Del(p) \
if(p){MemPool_Free(p);p=NULL;}


将一根对象指针先执行反注册,再摧毁,最后置空
//#define MemPool_Del(p) \
// if(p){MemPool_Free(p);p=NULL;}


//#define MemPool_Safe_Ary_Free(p) \
// if(p){MemPool_Free(p);delete[] p;p=NULL;}

#endif

***********************************************************************************************************************************************************

cpp如下

#include "MemPool.h"
#include "func.h"
















//全局函数
MemPool& GetMemPool()
{
static MemPool pool;
return pool;
}




void MemBlock::Print()
{
sync_printf("User Unfree file: %s,line: %d\n",m_strFileName,m_nLineNumber);
}


MemPool::MemPool()
{
m_Lock.EnableWrite(); //注意单写多读锁的使用,这是写锁
for(int nIndex =0;nIndex<17; ++nIndex )
{
//m_aryMallocSize[nIndex] = powl(2, 4+nIndex);
m_aryMallocSize[nIndex] = 0x00000001<<(4+nIndex);
m_aryMallocBlock[nIndex] = NULL;
m_aryFreeBlock[nIndex] = NULL;
}
m_Lock.DisableWrite();
}


MemPool::~MemPool()
{
this->PrintTree();


}




//根据块大小,取得索引
int MemPool::GetSizeIndex(int nSize)
{


for(int nIndex=0;nIndex<Max_MemBlock_Array;++nIndex)
{
//if(nSize <=powl(2,4+nIndex))
if(nSize <= 0x00000001<<(4+nIndex))
{
return nIndex;
}
}


return Max_MemBlock_Array;//系统分配超大内存


}




void* MemPool::Malloc(unsigned int nSize,const char* file,long line)
{

void* pData = NULL;//返回值
MemBlock* pTempBlock = NULL;


int nBlockIndex = this->GetSizeIndex(nSize + MemBlock_Size);
//使用系统内存分配极大的内存申请
if(nBlockIndex ==Max_MemBlock_Array)
{
m_Lock.EnableWrite(); //注意单写多读锁的使用,这是写锁
pData=(MemBlock*)malloc(nSize);
m_mallocInfo.OnSysMalloc();
m_Lock.DisableWrite();
return pData;//使用操作系统分配的内存.然后退出
}



m_Lock.EnableWrite();




//判断是否有可重复使用block
if(m_aryFreeBlock[nBlockIndex] == NULL)
{
//没有可重复使用的block
pTempBlock=(MemBlock*)malloc(m_aryMallocSize[nBlockIndex]);
pTempBlock->m_pData = pTempBlock + 1; //用户数据区的地址,实际上就是头地址+1,(类型为MemBlock*)
pTempBlock->m_nSizeIndex = nBlockIndex;//块大小的索引编号
pTempBlock->m_strFileName =file;
pTempBlock->m_nLineNumber =line;
pData =  pTempBlock->m_pData;//返回给用户使用的指针






//已分配出去的block使用双向链表
pTempBlock->m_pNext = m_aryMallocBlock[nBlockIndex];//将原来的链表头(即NULL)设置为链表第二个元素
pTempBlock->m_pFront = NULL;
if(m_aryMallocBlock[nBlockIndex])
{
m_aryMallocBlock[nBlockIndex]->m_pFront =pTempBlock;//旧链表头的front指向新建的block
}

m_aryMallocBlock[nBlockIndex] = pTempBlock;//将新创建的block作为队列
m_mallocInfo.OnMalloc(m_aryMallocSize[nBlockIndex],true);
}
else
{
//有可重复使用的block 
//free 数组的元素向前移动
pTempBlock = m_aryFreeBlock[nBlockIndex];
m_aryFreeBlock[nBlockIndex] = m_aryFreeBlock[nBlockIndex]->m_pNext;//交换头指针


//设置块信息
pTempBlock->m_nSizeIndex = nBlockIndex;//块大小的索引编号(校正)
pTempBlock->m_strFileName =file;
pTempBlock->m_nLineNumber =line;
assert( pTempBlock->m_pData == pTempBlock + 1);
pData =  pTempBlock->m_pData;//返回给用户的指针




//已分配出去的block使用双向链表
pTempBlock->m_pNext = m_aryMallocBlock[nBlockIndex];//将原来的链表头(即NULL)设置为链表第二个元素
pTempBlock->m_pFront = NULL; 
if(m_aryMallocBlock[nBlockIndex])
{
m_aryMallocBlock[nBlockIndex]->m_pFront =pTempBlock;//旧链表头的front指向新建的block
}
m_aryMallocBlock[nBlockIndex] = pTempBlock;//将新创建的block作为队列
m_mallocInfo.OnMalloc(m_aryMallocSize[nBlockIndex]);
}
m_Lock.DisableWrite();
return pData;


}
//释放一个块
void MemPool::Free(void* pData)
{
//放入空闲区
MemBlock* pBlock =  (MemBlock*)(((char*)pData)-MemBlock_Size);

//验证是否是由本内存池分配的指针
if( pBlock->m_pData == pData )
{
//是由本内存池分配的内存
assert(pBlock->m_nSizeIndex>=0 && pBlock->m_nSizeIndex< Max_MemBlock_Array);
m_Lock.EnableWrite(); 
//步骤1.在已经分配的block链表中移除该block

MemBlock* pSavedNext= pBlock->m_pNext;
if(pSavedNext)
{
pSavedNext->m_pFront =pBlock->m_pFront;
}




MemBlock* pSavedFront =pBlock->m_pFront;
if(pSavedFront)
{
pSavedFront->m_pNext =pBlock->m_pNext;
}
else
{
//已经是链表头
m_aryMallocBlock[pBlock->m_nSizeIndex] = pSavedNext;
}


//步骤2.将新释放的block设置为的待用block的链表头
pBlock->m_pNext=m_aryFreeBlock[pBlock->m_nSizeIndex];//将原来的链表头作为链表的第二个元素
m_aryFreeBlock[pBlock->m_nSizeIndex]=pBlock;//将刚释放的block作为链表头
m_mallocInfo.OnFree(m_aryMallocSize[pBlock->m_nSizeIndex]);
m_Lock.DisableWrite();
}
else
{
//m_Lock.EnableWrite();
free(pData);
m_mallocInfo.OnSysFree();
//m_Lock.DisableWrite(); 
}
m_Lock.DisableWrite(); 


}
//显示整棵内存树的内容
void MemPool::PrintTree(void)
{


//输出每个内存块的详细信息
for(int nIndex =0; nIndex<Max_MemBlock_Array; ++nIndex)
{
MemBlock* pBlock=NULL;
pBlock= m_aryMallocBlock[nIndex];
while(pBlock != NULL)
{
pBlock->Print();
pBlock= pBlock->m_pNext;
}
}
//输出没有被用户释放的内存块的统计信息
for(int nIndex =0; nIndex<Max_MemBlock_Array; ++nIndex)
{
int nBlockCnt=0;
MemBlock* pBlock=NULL;
pBlock= m_aryMallocBlock[nIndex];
while(pBlock != NULL)
{
++nBlockCnt;
pBlock= pBlock->m_pNext;
}
sync_printf("User UnFree MemBlock%d: now malloc size=%d\n",nIndex,nBlockCnt);
}


//输出等待分配的内存块统计信息
for(int nIndex =0; nIndex<Max_MemBlock_Array; ++nIndex)
{
int nBlockCnt=0;
MemBlock* pBlock=NULL;
pBlock= m_aryFreeBlock[nIndex];
while(pBlock != NULL)
{
++nBlockCnt;
pBlock= pBlock->m_pNext;
}
sync_printf("Waiting For Malloc MemBlock%d: now malloc size=%d\n",nIndex,nBlockCnt);
}


}
//显示统计信息
void MemPool::PrintInfo(void)
{
sync_printf("MemPool: now malloc size=%d,\
now malloc cnt=%d,\
   block cnt = %d,\
sys malloc cnt=%d\n",\
m_mallocInfo.m_nCurMallocSize, //当前分配出去的内存块的大小
m_mallocInfo.m_nCurMallocCnt,//当前分配出去的内存块的个数
m_mallocInfo.m_nBlockCnt, //内存块的总数(个数也是总数,因为内存池中没有销毁)
m_mallocInfo.m_nSysMallocCnt); //通过系统分配的指针个数
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值