内存池简单实现(一)

概念

内存池就是在程序启动时,预先向堆中申请一部分内存,交给一个管理对象。在程序运行中,需要时向管理对象“借”,不需要时“还”给管理对象。
原理很简单,关键是怎样才能高效地“借”、“还”。

实例场景

在C/S服务器中,需要频繁地收发数据包。而数据包的内存采用原始的new-delete模式,会大大降低服务器性能,所以想到了使用内存池技术。
在此实例中有几个硬性条件:
1、数据包最大长度可预期,所以内存池中块的大小暂且规定为1024。
2、内存池适用于多线程情况。也就是说,可以同时有多人“借”同一个空闲块。

设计思路

一、内存存放结构
内存池简单实现(一)_第1张图片
二、第一次“借”
遍历内存池,发现块1空闲(可以“借”出去)。标记块1为“已借”,记录此时位置(1,1)。然后,把块1“借出去”。
内存池简单实现(一)_第2张图片
三、连续几次“借”出,中途可能有几次归“还”
“借”的时候都是在上次“借”出的位置基础上往后遍历,运气差会遍历到末尾。当然,多线程嘛,中途可能有几次归还,对借出也没干扰。归还时,标记块的状态为“空闲”即可。状态为bool值,在一读一写的情况下,不需要加锁。怎么说时一读一写呢?借的时候需要加锁(一读),借出后,只可能借到的人写(一写)。
内存池简单实现(一)_第3张图片
四、遍历到末尾了
在借借还还中,总会出现这种情况。当前位置(3,1),后边的块全部标记为“已借”。很明显,这样会直接遍历到末尾。后边没数据了,只有看看头部到(3,1)这部分数据块有空闲的没。很幸运的是,遍历到了(1,2)空闲。
内存池简单实现(一)_第4张图片
五、没有空闲的块了
当从当前位置往后遍历,没有空闲块。然后在从头部遍历到当前位置,依然没有空闲块。说明,该添加新的内存到内存池了。然后,直接返回刚申请内存的第一块。
内存池简单实现(一)_第5张图片

说点啥

这只是众多思路中的一个。有瑕疵,却想不出更好的办法。麻烦的就是需要考虑多线程,有多线程就得加锁,加锁就是变相的单线程,就会影响效率。这里我只给借内存加了锁,还内存可以不加锁。先尝试这多写几个实例出来,比比哪种方法更高效。

代码

这里采用模版类,通用性更强。编译通过了,但没有使用过。

template
struct sBlock
{
    T       t;      //放在头部,方便指针强转换
    char    flag;   //可用标志, 0:可用 1:不可用
};
#ifndef MEMPOOL_H
#define MEMPOOL_H

#include 
#include 
using namespace std;

template
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 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* pbk = (sBlock*)p;
        pbk->flag = 0;      //原子操作,一读一写,不需要加锁
    }

private:
    void addBuff()
    {
        for (int i = 0; i < m_baseBuffCount; i++)
        {
            int size = sizeof(sBlock) * m_baseBlockCount;
            sBlock* pBlocks = (sBlock*)malloc(size);
            memset(pBlocks, 0, size);

            m_buffs.push_back(pBlocks);
        }
    }

private:
    vector*>  m_buffs;
    unsigned int        m_curBuffIndex;     //当前正在使用的大内存索引
    unsigned int        m_curBlockIndex;    //当前正在使用的块索引
    unsigned int        m_baseBuffCount;    //大内存基数量,每次新增大内存数
    unsigned int        m_baseBlockCount;   //每一个大内存包含多少个块
    mutex               m_mt;
};

#endif // MEMPOOL_H

你可能感兴趣的:(00,学习小记)