欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内存池简单实现(一)

程序员文章站 2022-05-13 08:14:58
...

概念

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

实例场景

在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
相关标签: 内存池