内存池简单实现(一)
概念
内存池就是在程序启动时,预先向堆中申请一部分内存,交给一个管理对象。在程序运行中,需要时向管理对象“借”,不需要时“还”给管理对象。
原理很简单,关键是怎样才能高效地“借”、“还”。
实例场景
在C/S服务器中,需要频繁地收发数据包。而数据包的内存采用原始的new-delete模式,会大大降低服务器性能,所以想到了使用内存池技术。
在此实例中有几个硬性条件:
1、数据包最大长度可预期,所以内存池中块的大小暂且规定为1024。
2、内存池适用于多线程情况。也就是说,可以同时有多人“借”同一个空闲块。
设计思路
一、内存存放结构
二、第一次“借”
遍历内存池,发现块1空闲(可以“借”出去)。标记块1为“已借”,记录此时位置(1,1)。然后,把块1“借出去”。
三、连续几次“借”出,中途可能有几次归“还”
“借”的时候都是在上次“借”出的位置基础上往后遍历,运气差会遍历到末尾。当然,多线程嘛,中途可能有几次归还,对借出也没干扰。归还时,标记块的状态为“空闲”即可。状态为bool值,在一读一写的情况下,不需要加锁。怎么说时一读一写呢?借的时候需要加锁(一读),借出后,只可能借到的人写(一写)。
四、遍历到末尾了
在借借还还中,总会出现这种情况。当前位置(3,1),后边的块全部标记为“已借”。很明显,这样会直接遍历到末尾。后边没数据了,只有看看头部到(3,1)这部分数据块有空闲的没。很幸运的是,遍历到了(1,2)空闲。
五、没有空闲的块了
当从当前位置往后遍历,没有空闲块。然后在从头部遍历到当前位置,依然没有空闲块。说明,该添加新的内存到内存池了。然后,直接返回刚申请内存的第一块。
说点啥
这只是众多思路中的一个。有瑕疵,却想不出更好的办法。麻烦的就是需要考虑多线程,有多线程就得加锁,加锁就是变相的单线程,就会影响效率。这里我只给借内存加了锁,还内存可以不加锁。先尝试这多写几个实例出来,比比哪种方法更高效。
代码
这里采用模版类,通用性更强。编译通过了,但没有使用过。
template<class T>
struct sBlock
{
T t; //放在头部,方便指针强转换
char flag; //可用标志, 0:可用 1:不可用
};
#ifndef MEMPOOL_H
#define MEMPOOL_H
#include <vector>
#include <mutex>
using namespace std;
template<class T>
class MemPool
{
public:
MemPool()
: m_curBuffIndex(0)
, m_curBlockIndex(0)
, m_baseBuffCount(0)
, m_baseBlockCount(0)
{}
~MemPool() {}
bool init(unsigned int buffCount, unsigned int blockCount)
{
m_baseBuffCount = buffCount;
m_baseBlockCount = blockCount;
addBuff();
return true;
}
T* mallocT()
{
lock_guard<mutex> lk(m_mt);
//遍历后方
for ( ; m_curBuffIndex < m_buffs.size(); ++m_curBuffIndex)
{
for ( ; m_curBlockIndex < m_baseBlockCount; ++m_curBlockIndex)
{
if (m_buffs[m_curBuffIndex][m_curBlockIndex].flag == 0)
{
m_buffs[m_curBuffIndex][m_curBlockIndex].flag = 1;
return m_buffs[m_curBuffIndex][m_curBlockIndex];
}
}
m_curBlockIndex = 0;
}
//遍历前方
int tempIndex = m_curBuffIndex;
for (m_curBuffIndex = 0; m_curBuffIndex < tempIndex; ++m_curBuffIndex)
{
for (; m_curBlockIndex < m_baseBlockCount; ++m_curBlockIndex)
{
if (m_buffs[m_curBuffIndex][m_curBlockIndex].flag == 0)
{
m_buffs[m_curBuffIndex][m_curBlockIndex].flag = 1;
return m_buffs[m_curBuffIndex][m_curBlockIndex];
}
}
m_curBlockIndex = 0;
}
//没有空闲的内存块,申请新内存
int tempSize = m_buffs.size();
addBuff();
m_curBuffIndex = tempSize;
m_buffs[m_curBuffIndex][m_curBlockIndex].flag = 1;
return m_buffs[m_curBuffIndex][m_curBlockIndex];
}
void freeT(T* p)
{
memset(p, 0, sizeof(T));
sBlock<T>* pbk = (sBlock<T>*)p;
pbk->flag = 0; //原子操作,一读一写,不需要加锁
}
private:
void addBuff()
{
for (int i = 0; i < m_baseBuffCount; i++)
{
int size = sizeof(sBlock<T>) * m_baseBlockCount;
sBlock<T>* pBlocks = (sBlock<T>*)malloc(size);
memset(pBlocks, 0, size);
m_buffs.push_back(pBlocks);
}
}
private:
vector<sBlock<T>*> m_buffs;
unsigned int m_curBuffIndex; //当前正在使用的大内存索引
unsigned int m_curBlockIndex; //当前正在使用的块索引
unsigned int m_baseBuffCount; //大内存基数量,每次新增大内存数
unsigned int m_baseBlockCount; //每一个大内存包含多少个块
mutex m_mt;
};
#endif // MEMPOOL_H