c++内存池无锁

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++内存池无锁相关的知识,希望对你有一定的参考价值。

使用自己的内存管理方式

最近做监控展示,接收包,解码,播放,多画面短时间轮询,例如10秒,在频繁使用申请内存和删除内存的过程中,不可避免产生性能上的损耗,操作系统在无法申请到内存时,如果自己的程序没有判断申请内存是否成功,后面会产生一系列的问题,最好的解决方案是使用自己的内存管理方式。

定义几个数据结构

enum

    en_emptying = 0,
    en_writing,
    en_canreadings
;
struct smem

    char v_status;
    char v_i_flag;
    char r1;
    char r2;
    int v_len = 0;
    uint32_t v_pts = 0;
    uint8_t *v_data = NULL;
;

typedef std::shared_ptr<smem> ptr_smem;

定义内存池

包含这样的数据指针:
1 数据内存头部
2 数据内存结尾
3 写指针
4 读指针
我们限定只有一个线程读,一个线程写,并且避开读写内存管理的锁定。
我们的数据有两种,一种是数据信息info,也就是我们需要的内存起始,内存长度,以及其他附加数据信息,比如这个包的pts值,这个包的状态信息,是否是关键帧等等,这些东西我们必须要做内存对齐规整,另一种数据就是实际数据,比如h264包,h265包,需要解码的视频数据(或者其他数据)

这其中有两种方式来做这个池,一个是将数据信息直接写入内存的起始位置,一种是将数据信息放入队列,直接放入内存池中也是可以的,这种写法避免了信息队列的维护,当然,信息队列已经是很小的内存了,我们提出两种方式,可以根据需求选择。

方式一,写入内存池

v_write 是内存写入地址,每次写完就往后跳,写入时首先要知道不能超过剩余的空间,否则要等读指针结束,或者让读指针快速通过。写入方法:

   uint8_t *m = v_write;
   *(char*)(m) = (char)en_emptying;
   m +=sizeof(char);
   *(char*)(m) = (char)0;
   m +=sizeof(char)*3;
   *(int*)m = 0;
   m +=sizeof(int);
   *(uint32_t*)m = 0;

以上方法就是将信息数据写入内存池的数据头部,然后再写入数据。这种方式比较考验程序员的基本功,其实也不推荐,另外一种方式直接写入队列中,逻辑清晰,该队列只是一个小型数据集,不会引起性能波动。

以下使用队列和内存池来存放内存

看代码吧,读写指针一定要离开一定的距离,开始时是在一起的,以下代码已经经过验证,当然,在使用的时候还是需要一定的技巧的。

struct s_mem_pool

    //qianbo :just one thread read,one thread write ,otherwise error occur
    uint8_t *v_data = NULL;
    uint8_t *v_end = NULL;
    int v_len = 0;;
    uint8_t *v_write = NULL;
    uint8_t *v_read  = NULL;

    QMutex v_mux;
    std::atomic_int v_framenum;
    std::queue<ptr_smem>v_i;
    void init_mem(int memlen)
    
        if(v_data!=NULL)
        
            if(v_len < memlen)
            
                free(v_data);
                v_data = NULL;
            
        


        v_len = memlen;
        if(v_data == NULL)
            v_data = (uint8_t*)malloc(memlen);

        v_end = v_data + memlen;
        v_write = v_data;
        v_read = v_data;
        v_framenum = 0;
        v_mux.lock();
        while(!v_i.empty())
        
            v_i.pop();
        
        v_mux.unlock();



    ~s_mem_pool()
    
        clearqueue();
        if(v_data!=NULL)
            free(v_data);
    


    bool push(uint8_t *data, int len,uint32_t pts,bool i_flag)
    
#define EX_LEN (sizeof(char)*4 +sizeof(int)+sizeof(uint32_t))
#define NEEDLEN (len)

#define WRITE_INFO \\
        ptr_smem mem = std::make_shared<smem>();\\
        mem->v_data = v_write;\\
        mem->v_len = len;\\
        mem->v_pts = pts;\\
        mem->v_i_flag = i_flag; \\
        mem->v_status = en_writing;\\
        v_mux.lock();\\
        v_i.emplace(mem);\\
        v_mux.unlock();

#define WRITE_INFO2 \\
    uint8_t *m = v_write; \\
    *(char*)(m) = (char)en_writing; \\
    m +=1; \\
    *(char*)(m) = (char)i_flag;\\
    m +=3; \\
    *(int*)m = len; \\
    m +=4; \\
    *(uint32_t*)m = pts; \\
    m+= 4;\\
    v_write = m;


       if(v_write >= v_read)
       
           if((v_end - v_write) > (long)(NEEDLEN))
           

            memcpy(v_write,data,len);
            WRITE_INFO
             v_write   += (NEEDLEN);
             v_framenum++;
             return true;
           
           else // the left mem is not enough
           
               //qInfo()<<"left mem is not enough";
               if((v_read -v_data) > (long)(NEEDLEN))
               
                   v_write = v_data;
                   memcpy(v_write,data,len);
                   WRITE_INFO
                   v_write += NEEDLEN;
                   v_framenum++;
                   return true; //from the head
               
               else
               
                   qInfo()<<"not enough memory 0 :num"<<v_framenum;
                   return false;
               
           
       
       else //read>write
       
            if((v_read - v_write) > (long)(NEEDLEN))
            
                memcpy(v_write,data,len);
                WRITE_INFO
                v_write += (NEEDLEN);
                v_framenum++;
                //IncNumber();
                return true;
            
            qInfo()<<"not enough memory 1 clear the buffer the len is "<< NEEDLEN;

            return false;
       
    
    ptr_smem get()
    
        ptr_smem sm = nullptr;
        v_mux.lock();
        if(!v_i.empty())
        
            sm =  v_i.front();
            v_i.pop();
            v_read = sm->v_data;
        
         v_mux.unlock();
         v_framenum--;
        return sm;

    int size()
    
        return v_framenum;
    
    void clearqueue()
    
        v_mux.lock();
        if(!v_i.empty())
        
            v_i.pop();
        
        v_mux.unlock();
    
;

以上是关于c++内存池无锁的主要内容,如果未能解决你的问题,请参考以下文章

c++千万数据级别正确使用无锁队列,避免内存撕碎

c++千万数据级别正确使用无锁队列,避免内存撕碎

盲猜原子变量内存屏障内存模型锁之间的关系

C++ 无锁队列与多线程崩溃

无锁队列真的比有锁队列快吗c++ linux后台开发

带危险指针的无锁内存回收