c++ 加二级缓存 环形缓冲区

2020-04-03 08:45:22 zouyang85457013 阅读数 76

分享一个在工作中用到的简单的环形缓冲区。构造简单,可以改造到Qt中使用,也可以直接在VS下使用。

所谓环形缓冲区,其实就是带有标识缓冲区中数据头、尾位置的缓冲区,这个缓冲区根据业务的不同,要设置的稍微大一点,不能一有数据过来就填满了,这样就失去使用的意义了。

在实际工作中,我们使用TCP和设备进行通信,如果数据流量过大,可以先把数据接收到数据缓冲区中,处理之后再取出。我们定义的包协议可以采用定长包,可以采用不定长度的包,环形缓冲区都能处理。

//头文件定义
class CircleBuffer
{
public:
    CircleBuffer();
    ~CircleBuffer();

private:
    char *buf;                 //指向环形缓冲区的指针
    int head;                  //缓冲区头部索引
    int rear;                  //缓冲区尾部索引
    int maxBuf;                //缓冲区的最大值(根据业务不同,设置合适的值)
    int packHeadLength;        //数据包头长度(如果是定长包,那么这里可以是整个数据包。如果是不
                               //定长的,那么这里就是包头的长度)
    char processBuf[1024];     //缓冲区中的数据就放在这里进行处理,然后通过回调或者信号槽的方 
                               //式传递出去
protected:
    inline int  getDataLength();               //缓冲区中数据的长度
    inline int  getFreeBufferLength();         //获取缓冲区中剩余的长度   
    inline bool Empty();                       //缓冲区是否为空
    inline void copyData(char *data,int len);  //把接收到的数据拷贝到缓冲区中
    inline void getData(int len);              //把缓冲区中的数据拷贝到processBuf中
    inline void moveHead(int len);             //处理完成后要移动头部索引的位置
    void processData();                        //实际处理数据的过程

//外部调用接口
public:
     void pushData(char *data,int len);        //从TCP收到的数据放入环形缓冲区中   
     void resetBuffer();                       //重置缓冲区      
};
//源文件实现
CircleBuffer::CircleBuffer()
{
    rear           = 0;
    head           = 0;
    maxBuf         = 1024 * 5;
    buf            = new char[maxBuf];
    packHeadLength = sizeof("你自己定义的数据包头");
}

CircleBuffer::~CircleBuffer()
{
    if(buf)
    {
        delete[] buf;
        buf = NULL;
    }
}

int CircleBuffer::getDataLength()
{
    if(rear >= head)
    {
        return rear - head;
    }
    else //多次存取之后尾部索引有可能比头部索引要小
    {
        return rear-head + maxBuf;
    }
}

qint16 CircleBuffer::getFreeBufferLength()
{
    return maxBuf-getDataLength();
}

bool CircleBuffer::Empty()
{
    return head==rear;
}

void CircleBuffer::copyData(char *data, int len)
{
    //按字节拷贝
    for(int i=0;i<len;i++)
    {
        buf[rear] = data[i];
        //注意这里不能用rear++,既然是环形缓冲区,如果填充到底部之后,底部的索引要回到头部
        rear = (rear + 1)% maxBuf;
    }
}

void CircleBuffer::getData(int len)
{
    int temp = head;//获取数据的时候不能改变头部索引的值,全部处理完成之后才改变头部索引的位置
    for(int i=0;i<len ;i++)
    {
        processBuf[i] = buf[temp];
        //这里和上面一样,头部的索引也有可能跑到底部,得让它能跑回来
        temp = (temp + 1)% maxBuf;
    }
}

void CircleBuffer::moveHead(int len)
{
    //看到了吧,这里用到的最多的就是这个%(取余符号了),这样就可以使头尾两个索引像一个环形一样
    head = (head + len)% maxBuf;
}

//实际处理数据的过程,这里可以根据需要进行修改
//这里的packetHeader对应的为你的包头类
void CircleBuffer::processData()
{
    while(getDataLength() >= packHeadLength)                     //当数据不少于一个包头时
    {
      getData(packHeadLengh);                                    //获得包头数据
      packetHeader *phead=(packetHeader *)processBuf;            //解出包头
      //数据是否合法(包头类提供一个检查数据是否合法的函数)
      if(phead->isVaild())                            
      {
          //这里可以获得整个数据包的长度
          int data_len = phead->getLength();
          if(data_len == 0)                                      //只有包头,没有数据
          {
              //1,通过回调或者信号槽通知调用者来处理
              //......
              //2, 移动头部索引位置
              moveHead(packHeadLength);
          }
          else
          {
              if(getDataLength() >= (data_len+packHeadLength))    //有包头,有数据
              {
                  getData(data_len+packHeadLength);
                  //1,通过回调或者信号槽通知调用者来处理
                  //......
                  //2, 移动头部索引位置
                  moveHead(data_len+packHeadLength);
              }
              else
                  break;                                          //不处理
          }
      }
      else
      {
          rear = 0;
          head = 0;
          //出错了,通过回调或者信号槽通知调用者来处理
          //..............
      }

    }
}


//提供给使用者的接口
//重置缓冲区
void CircleBuffer::resetBuffer()
{
    rear   = 0;
    head   = 0;
}

//处理数据
void CircleBuffer::pushData(char *data, int len)
{
    //1,拷贝数据到处理缓存,要考虑缓冲区剩余空间不够的情况
    if(getFreeBuflength() > len)
    {
        copyData(data,len);
    }
    else
    {
        //开辟更大空间,将原有数据复制到新的缓存
        char *p = new char[maxBuf + len];
        int  data_len = getDataLength();
        for(int i=0 ;i<data_len ;i++)
        {
            p[i] = buf[head];
            head = (head + 1)% maxBuf;
        }

        //更新各种数据
        head = 0;
        rear = data_len;
        maxBuf = maxBuf +len;
        delete[] buf;
        buf = p;
        //拷贝数据
        copyData(data,len);
    }

    //2, 最后再处理数据
    processData();
}

 

2018-10-23 21:13:43 qing666888 阅读数 1201

读写锁版本(推荐):https://blog.csdn.net/yunandsha/article/details/80925429

参考网络循环缓冲区类源码,修改了作者的两个bug,经测试,可以实现多线程并发读写。数据准确无误。

多线程并行读写环形缓冲区源代码例子
代码例子下载:环形缓冲区VC+Qt的项目代码
理论如下:
在这里插入图片描述
源码如下:
CCycleBuffer.h
//环形缓冲区头文件

//环形缓冲区头文件

#ifndef CCycleBuffer_H
#define CCycleBuffer_H
class CCycleBuffer
{
public:
	bool isFull();
	bool isEmpty();
	void empty();
	int getLength();
	CCycleBuffer(int size);
	virtual~CCycleBuffer();
	int write(char* buf, int count);
	int read(char* buf, int count);
	int getStart()
	{
		return m_nReadPos;
	}
	int getEnd()
	{
		return m_nWritePos;
	}

private:
	bool m_bEmpty, m_bFull;
	char* m_pBuf;
	int m_nBufSize;
	int m_nReadPos;
	int m_nWritePos;
	int test;
};
#endif// CCycleBuffer_H

CCycleBuffer.c

//环形缓冲区源文件
//尽量用C运行时库代码,改变了原作者基于WindowsAPI的代码。
//修改了原作者的两处错误,1是read函数最后的else 少了一个leftcount
//第二个错误,是write函数中,m_nWritePos 变量,他重新定义了一个。这些错误编译不会出错。
//但是运行起来,要了亲命啊。

#include "CCycleBuffer.h"
#include <assert.h>
#include <memory.h>
#include <QDebug>
// 定义 
CCycleBuffer::CCycleBuffer(int size)
{
   m_nBufSize = size;
   m_nReadPos = 0;
   m_nWritePos = 0;
   m_pBuf = newchar[m_nBufSize];
   m_bEmpty = true;
   m_bFull = false;
   test = 0;
}

CCycleBuffer::~CCycleBuffer()
{
   delete[] m_pBuf;
}

/************************************************************************/
/* 向缓冲区写入数据,返回实际写入的字节数                               */
/************************************************************************/
int CCycleBuffer::write(char* buf, int count)
{
   if (count <= 0)
   	return0;
   m_bEmpty = false;
   // 缓冲区已满,不能继续写入 
   if (m_bFull)
   {
   	return0;
   }
   else if (m_nReadPos == m_nWritePos)// 缓冲区为空时 
   {
   	/*                          == 内存模型 ==
   	   (empty)             m_nReadPos                (empty)
   	|----------------------------------|-----------------------------------------|
   		   m_nWritePos        m_nBufSize
   	*/
   	int leftcount = m_nBufSize - m_nWritePos;
   	if (leftcount > count)
   	{
   		memcpy(m_pBuf + m_nWritePos, buf, count);
   		m_nWritePos += count;
   		m_bFull = (m_nWritePos == m_nReadPos);
   		return count;
   	}
   	else
   	{
   		memcpy(m_pBuf + m_nWritePos, buf, leftcount);
   		m_nWritePos = (m_nReadPos > count - leftcount) ? count - leftcount : m_nWritePos;
   		memcpy(m_pBuf, buf + leftcount, m_nWritePos);
   		m_bFull = (m_nWritePos == m_nReadPos);
   		return leftcount + m_nWritePos;
   	}
   }
   else if (m_nReadPos < m_nWritePos)// 有剩余空间可写入 
   {
   	/*                           == 内存模型 ==
   	 (empty)                 (data)                     (empty)
   	|-------------------|----------------------------|---------------------------|
   	   m_nReadPos                m_nWritePos       (leftcount)
   	*/
   	// 剩余缓冲区大小(从写入位置到缓冲区尾) 

   	int leftcount = m_nBufSize - m_nWritePos;
   	int test = m_nWritePos;
   	if (leftcount > count)   // 有足够的剩余空间存放 
   	{
   		memcpy(m_pBuf + m_nWritePos, buf, count);
   		m_nWritePos += count;
   		m_bFull = (m_nReadPos == m_nWritePos);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return count;
   	}
   	else       // 剩余空间不足 
   	{
   		// 先填充满剩余空间,再回头找空间存放 
   		memcpy(m_pBuf + test, buf, leftcount);

   		m_nWritePos = (m_nReadPos >= count - leftcount) ? count - leftcount : m_nReadPos;
   		memcpy(m_pBuf, buf + leftcount, m_nWritePos);
   		m_bFull = (m_nReadPos == m_nWritePos);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return leftcount + m_nWritePos;
   	}
   }
   else
   {
   	/*                          == 内存模型 ==
   	 (unread)                 (read)                     (unread)
   	|-------------------|----------------------------|---------------------------|
   		m_nWritePos    (leftcount)    m_nReadPos
   	*/
   	int leftcount = m_nReadPos - m_nWritePos;
   	if (leftcount > count)
   	{
   		// 有足够的剩余空间存放 
   		memcpy(m_pBuf + m_nWritePos, buf, count);
   		m_nWritePos += count;
   		m_bFull = (m_nReadPos == m_nWritePos);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return count;
   	}
   	else
   	{
   		// 剩余空间不足时要丢弃后面的数据 
   		memcpy(m_pBuf + m_nWritePos, buf, leftcount);
   		m_nWritePos += leftcount;
   		m_bFull = (m_nReadPos == m_nWritePos);
   		assert(m_bFull);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return leftcount;
   	}
   }
}

/************************************************************************/
/* 从缓冲区读数据,返回实际读取的字节数                                 */
/************************************************************************/
int CCycleBuffer::read(char* buf, int count)
{
   if (count <= 0)
   	return0;
   m_bFull = false;
   if (m_bEmpty)       // 缓冲区空,不能继续读取数据 
   {
   	return0;
   }
   else if (m_nReadPos == m_nWritePos)   // 缓冲区满时 
   {
   	/*                          == 内存模型 ==
   	 (data)          m_nReadPos                (data)
     |--------------------------------|--------------------------------------------|
   	  m_nWritePos         m_nBufSize
   	*/
   	int leftcount = m_nBufSize - m_nReadPos;
   	if (leftcount > count)
   	{
   		memcpy(buf, m_pBuf + m_nReadPos, count);
   		m_nReadPos += count;
   		m_bEmpty = (m_nReadPos == m_nWritePos);
   		return count;
   	}
   	else
   	{
   		memcpy(buf, m_pBuf + m_nReadPos, leftcount);
   		m_nReadPos = (m_nWritePos > count - leftcount) ? count - leftcount : m_nWritePos;
   		memcpy(buf + leftcount, m_pBuf, m_nReadPos);
   		m_bEmpty = (m_nReadPos == m_nWritePos);
   		return leftcount + m_nReadPos;
   	}
   }
   else if (m_nReadPos < m_nWritePos)   // 写指针在前(未读数据是连接的) 
   {
   	/*                          == 内存模型 ==
   	 (read)                 (unread)                      (read)
   	|-------------------|----------------------------|---------------------------|
   	   m_nReadPos                m_nWritePos                     m_nBufSize
   	*/
   	int leftcount = m_nWritePos - m_nReadPos;
   	int c = (leftcount > count) ? count : leftcount;
   	memcpy(buf, m_pBuf + m_nReadPos, c);
   	m_nReadPos += c;
   	m_bEmpty = (m_nReadPos == m_nWritePos);
   	assert(m_nReadPos <= m_nBufSize);
   	assert(m_nWritePos <= m_nBufSize);
   	return c;
   }
   else          // 读指针在前(未读数据可能是不连接的) 
   {
   	/*                          == 内存模型 ==
   	   (unread)                (read)                      (unread)
   	|-------------------|----------------------------|---------------------------|
   		m_nWritePos                  m_nReadPos                  m_nBufSize

   	*/
   	int leftcount = m_nBufSize - m_nReadPos;
   	if (leftcount > count)   // 未读缓冲区够大,直接读取数据 
   	{
   		memcpy(buf, m_pBuf + m_nReadPos, count);
   		m_nReadPos += count;
   		m_bEmpty = (m_nReadPos == m_nWritePos);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return count;
   	}
   	else       // 未读缓冲区不足,需回到缓冲区头开始读 
   	{
   		memcpy(buf, m_pBuf + m_nReadPos, leftcount);
   		m_nReadPos = (m_nWritePos >= count - leftcount) ? count - leftcount : m_nWritePos;
   		memcpy(buf + leftcount, m_pBuf, m_nReadPos);
   		m_bEmpty = (m_nReadPos == m_nWritePos);
   		assert(m_nReadPos <= m_nBufSize);
   		assert(m_nWritePos <= m_nBufSize);
   		return leftcount + m_nReadPos;
   	}
   }
}

/************************************************************************/
/* 获取缓冲区有效数据长度                                               */
/************************************************************************/
int CCycleBuffer::getLength()
{
   if (m_bEmpty)
   {
   	return0;
   }
   else if (m_bFull)
   {
   	return m_nBufSize;
   }
   else if (m_nReadPos < m_nWritePos)
   {
   	return m_nWritePos - m_nReadPos;
   }
   else
   {
   	return m_nBufSize - m_nReadPos + m_nWritePos;
   }
}

void CCycleBuffer::empty()
{
   m_nReadPos = 0;
   m_nWritePos = 0;
   m_bEmpty = true;
   m_bFull = false;
}

bool CCycleBuffer::isEmpty()
{
   return m_bEmpty;
}

bool CCycleBuffer::isFull()
{
   return m_bFull;
}

原帖地址:https://blog.csdn.net/devday/article/details/5258697

2018-12-03 10:30:52 weiyu00 阅读数 481

https://blog.csdn.net/weiyu00/article/details/84750560

//RingBuffer.h
#pragma once

template <class T> 
class CRingBuffer
{
public:
	CRingBuffer()
	{
		m_pBuffer = NULL;
		m_nSize = 0;
		m_nUnreadSize = 0;
		m_nWritePos = 0;
		m_nReadPos = 0;
	}

	~CRingBuffer()
	{
		m_cs.Lock();
		if (m_pBuffer != NULL)
		{
			delete [] m_pBuffer;
			m_pBuffer = NULL;
			m_nSize = 0;
			m_nUnreadSize = 0;
			m_nWritePos = 0;
			m_nReadPos = 0;
		}
		m_cs.Unlock();
	}

	//清空环形缓冲区
	void Clear()
	{
		m_cs.Lock();
		m_nUnreadSize = 0;
		m_nWritePos = 0;
		m_nReadPos = 0;
		m_cs.Unlock();
	}

	//设置环形缓冲区
	//nSize:缓冲区大小
	void SetBuffer(UINT nSize)
	{
		m_cs.Lock();
		if (m_pBuffer != NULL)
		{
			delete [] m_pBuffer;
			m_pBuffer = NULL;
			m_nSize = 0;
			m_nUnreadSize = 0;
			m_nWritePos = 0;
			m_nReadPos = 0;
		}

		m_pBuffer = new T[nSize];
		if (m_pBuffer != NULL)
		{
			memset(m_pBuffer, 0x00, nSize*sizeof(T));
			m_nSize = nSize;
			m_nUnreadSize = 0;
			m_nWritePos = 0;
			m_nReadPos = 0;
		}
		m_cs.Unlock();
	}
	//写入数据到环形缓冲区
	//返回值:写入的数据大小
	UINT WriteData(const T* pT, UINT nSize = 1)
	{
		m_cs.Lock();
		if (nSize > m_nSize - m_nUnreadSize)//缓冲区大小不足时,仅写入最前面的数据
			nSize = m_nSize - m_nUnreadSize;

		if (nSize > 0)
		{
			if (m_nWritePos + nSize <= m_nSize)
			{
				memcpy(&m_pBuffer[m_nWritePos], pT, nSize*sizeof(T));
			}
			else
			{
				memcpy(&m_pBuffer[m_nWritePos], pT, (m_nSize - m_nWritePos)*sizeof(T));
				if (nSize - (m_nSize - m_nWritePos) > 0)
					memcpy(m_pBuffer, &pT[m_nSize - m_nWritePos], (nSize - (m_nSize - m_nWritePos))*sizeof(T));
			}

			m_nUnreadSize += nSize;
			m_nWritePos += nSize;
			if (m_nWritePos >= m_nSize)
				m_nWritePos %= m_nSize;
		}

		m_cs.Unlock();
		return nSize;
	}
	//从环形缓冲区读取数据
	//nSize:缓冲区大小
	//返回值:读取的数据大小
	UINT ReadData(T* pT, UINT nSize = 1)
	{
		m_cs.Lock();
		if (nSize > m_nUnreadSize || m_nUnreadSize == 0)
			nSize = m_nUnreadSize;

		if (nSize > 0)
		{
			if (m_nReadPos + nSize <= m_nSize)
			{
				memcpy(pT, &m_pBuffer[m_nReadPos], nSize*sizeof(T));
			}
			else
			{
				memcpy(pT, &m_pBuffer[m_nReadPos], (m_nSize - m_nReadPos)*sizeof(T));
				if (nSize - (m_nSize - m_nReadPos) > 0)
					memcpy(&pT[m_nSize - m_nReadPos], m_pBuffer, (nSize - (m_nSize - m_nReadPos))*sizeof(T));
			}

			m_nUnreadSize -= nSize;
			m_nReadPos += nSize;
			if (m_nReadPos >= m_nSize)
				m_nReadPos %= m_nSize;
		}

		m_cs.Unlock();
		return nSize;
	}
private:
	T* m_pBuffer;			//缓冲区
	UINT m_nSize;			//缓冲区大小

	UINT m_nUnreadSize;		//未读取的数据大小

	UINT m_nWritePos;		//写入下标
	UINT m_nReadPos;		//读取下标

	CComAutoCriticalSection m_cs;//线程锁
};
2018-05-16 17:45:23 u014337397 阅读数 422

环形缓冲区

  • 什么是环形缓冲区
    环形缓冲区用于表示一个固定尺寸、头尾相连的缓冲区,通常用于缓存数据。环形缓冲区的主要特点是最大缓存数据量是固定的,当缓冲区满之后,会自动删除头部数据。
  • 应用场景
    总的来说,只要是固定长度的数据缓存,都可以使用环形缓冲区。个人用的较多的地方主要是视频缓存。

环形缓冲区代码示例

  • 具体代码
#include <mutex>;
#include <iostream>;
using namespace std;

//类名:环形缓冲区类
//功能:实现任意类的环形缓冲区存储
template<typename T>
class RingBuffer
{
public:
    //函数功能:环形缓冲区构造函数
    //参数:    int iMaxBufferSize[IN]                   -- 环形缓冲区最大长度,长度应大于0
    //返回值:  无
    RingBuffer(int iMaxBufferSize):
        m_MaxBufSize(iMaxBufferSize),
        m_Buffer(NULL),
        m_CopyBuffer(NULL)
    {
        _ASSERTE(m_MaxBufSize > 0);

        this->m_Buffer = new T[m_MaxBufSize];

        _ASSERT(this->m_Buffer != NULL);

        this->m_CopyBuffer = new T[m_MaxBufSize];

        _ASSERT(this->m_CopyBuffer != NULL);
    }

    //函数功能:环形缓冲区析构函数
    //参数:    无
    //返回值:  无
    ~RingBuffer()
    {
        this->m_mtElement.lock();
        if (m_Buffer != NULL)
        {
            if (m_MaxBufSize>1)
            {
                delete[] m_Buffer;
                delete[] m_CopyBuffer;
            }
            else if (1==m_MaxBufSize)
            {
                delete m_Buffer;
                delete m_CopyBuffer;
            }

            m_Buffer = NULL;
            m_CopyBuffer = NULL;
        }
        this->m_mtElement.unlock();
    }

    //函数功能:环形缓冲区析构函数
    //参数:    const T element[IN]                   -- 要插入的元素
    //返回值:  无
    bool Append(const T element)
    {
        bool bRet = true;
        this->m_mtElement.lock();
        if (this->m_CurElementNum<=(this->m_MaxBufSize-1))
        {
            //尚有富余空间,直接插入
            this->m_CurElementNum++;
            this->m_Buffer[this->m_CurElementNum - 1] = element;
        }
        else
        {
            //空间已满,需要去掉最前面的,然后插入
            memcpy(this->m_CopyBuffer, &(this->m_Buffer[1]), (this->m_CurElementNum - 1) * sizeof(T));
            this->m_CopyBuffer[this->m_CurElementNum - 1] = element;

            memcpy(this->m_Buffer, this->m_CopyBuffer, this->m_CurElementNum * sizeof(T));
        }
        this->m_mtElement.unlock();
        return bRet;
    }

    //函数功能:环形缓冲区打印
    //参数:    const T element[IN]                   -- 要插入的元素
    //返回值:  无
    bool Print()
    {
        bool bRet = true;
        this->m_mtElement.lock();
        cout << "环形缓冲区结果如下:" << endl;
        for (int i = 0; i < this->m_CurElementNum; i++)
        {
            cout << "    "<<this->m_Buffer[i] << endl;
        }
        cout << endl;
        this->m_mtElement.unlock();
        return bRet;
    }

    //函数功能:检查序列中是否包容某个元素
    //参数:    const T element[IN]                   -- 要检查的元素
    //返回值:  bool                                  -- 包含指定元素,则返回True;否则返回False
    bool Contains(const T element)
    {
        bool bRet = false;
        this->m_mtElement.lock();
        for (int i = 0; i < this->m_CurElementNum; i++)
        {
            if (element==this->m_Buffer[i])
            {
                bRet = true;
                break;
            }
        }
        this->m_mtElement.unlock();
        return bRet;
    }

    //函数功能:获取指定序列中的值
    //参数:    const T element[IN]                   -- 要检查的元素
    //返回值:  bool                                  -- 包含指定元素,则返回True;否则返回False
    T& operator[](int index)
    {
        T ret;
        this->m_mtElement.lock();
        _ASSERTE(index <= this->m_CurElementNum - 1);
        ret = this->m_Buffer[index];
        this->m_mtElement.unlock();
        return ret;
    }
private:
    int m_MaxBufSize;                      //环形缓冲区容最大长度
    int m_CurElementNum;                   //当前元素数量
    T* m_Buffer;                           //环形缓冲区
    T* m_CopyBuffer;                       //复制缓冲区,为了加快速度
    mutex m_mtElement;                     //互斥锁
};

int main()
{
    RingBuffer<int>* pTempRingBuffer = new RingBuffer<int>(100);

    if (pTempRingBuffer!=NULL)
    {
        for (int i = 0; i < 100; i++)
        {
            pTempRingBuffer->Append(i);
        }

        for (int i = 0; i < 10; i++)
        {
            cout << (*pTempRingBuffer)[i] << endl;
        }

        pTempRingBuffer->Print();
        delete pTempRingBuffer;
    }

    getchar();
    return 0;
}
  • 相关说明
    该类编译环境为VS2015,可以正常编译通过
    该类支持多线程操作,为了保证多线程的安全性,使用MUTEX互斥锁,如果是早期开发环境,也可以改为临界区
    为了降低移植难度,没有使用容器类,直接通过动态数组来存储缓存数据
    在插入数据时,为了减少频繁的数据移动,采用了一个备份数组来交换数据
  • 测试平台
    测试平台:操作系统:Windows7
    CPU: i5 6400 2.70GHZ
    内存: 8G
  • 性能指标
    当缓冲区长度为100,单线程的插入性能如下:
    插入一亿个int数据的耗时:7950ms
    插入单个int数据耗时:0.0000795ms。
2016-06-26 20:38:28 LIFEXX 阅读数 4894

        前段时间有个项目要实现一个基于live555的rtspserver,部分功能要用到环形缓冲区,网上看了一些blog,大部分是实验性质的,不太敢用,原理比较简单,所以就自己写了一个;

        实现环形缓冲区的关键点:

        1. 一个线程读,一个线程写

        2. 读线程维护读指针,写线程维护写指针

        3. 数据一致性

        3.1 写线程写数据时,要先确定读指针;读线程读数据时,要先确定写指针;

        这里写的可能比较拗口,其实就是 写线程写数据时,需要多次用使用读指针,比如说计算ringbuf可用空间,是否达到ringbuf末尾等等;由于读指针是在读线程里实时更新的,所以写线程写数据函数多次使用读指针时,读指针的值会不一样;解决这个问题只需要在 读/写 函数 开始处 定义一个临时变量,保存 读/写 指针的值,后续计算都使用该临时变量就OK了;

        3.2 这里多说几句废话

        ringbuf实现类中可能不仅会开放 ReadData/WriteData接口,还会有类似GetRingbufDataLen的函数,这类函数内部肯定要使用读写指针,这个时候就要注意,如果ReadData/WriteData函数调用这类函数,要保证它们和ReadData/WriteData 使用的读写指针的值是一致的。

说完废话贴代码:

//left 1Byte for guard
int RingBuffer::GetFreeBufferBytes(int iRidx, int iWidx)
{
	if (iRidx == iWidx)
	{
		return m_uBufferSize-1;
	}
	else if (iWidx > iRidx)
	{
		return (iRidx - iWidx + m_uBufferSize - 1);
	}
	else
	{
		return (iRidx - iWidx - 1);
	}
}

//this func can  write to the addr = ridx-1 (at most)
int RingBuffer::Write(const unsigned char* pBuf, unsigned writeLen)
{
	//m_pBuffer may alloc memory failed
	if (!m_pBuffer)
	{
		return -1;
	}

	int iRidx = m_iRIdx;
	int iWidx = m_iWidx;
	if (!pBuf || 0 == writeLen || GetFreeBufferBytes(iRidx, iWidx) < writeLen)
	{
		return -1;
	}

	int len1 = 0;
	if (m_iWidx < iRidx)
	{
		memcpy(&m_pBuffer[m_iWidx], pBuf, writeLen);
		m_iWidx += writeLen;
	}
	else
	{
		len1 = m_uBufferSize - m_iWidx;
		if (writeLen <= len1)
		{
			memcpy(&m_pBuffer[m_iWidx], pBuf, writeLen);
			m_iWidx += writeLen;
		}
		else
		{
			memcpy(&m_pBuffer[m_iWidx], pBuf, len1);
			memcpy(m_pBuffer, pBuf + len1, writeLen - len1);
			m_iWidx = writeLen - len1;
		}
	}

	return writeLen;
}

//
int RingBuffer::Read(unsigned char* pBuf, unsigned readLen)
{
	//m_pBuffer may alloc memory failed
	if (!m_pBuffer)
	{
		return -1;
	}

	if (!pBuf)
	{
		return -1;
	}

	int iWidx = m_iWidx;
	int iRidx = m_iRIdx;
	int bufferDataLen = m_uBufferSize - GetFreeBufferBytes(iRidx, iWidx) - 1;
	if (bufferDataLen <= readLen)
	{
		//can not use readall here, because GetFreeBufferBytes func and readall func may use the different
		//ridx and widx
		return ReadToWidx(pBuf, iWidx);
	}
	else  
	{
		if (m_iRIdx < iWidx)
		{
			memcpy(pBuf, &m_pBuffer[m_iRIdx], readLen);
			m_iRIdx += readLen;
		} 
		else
		{
			int len1 = m_uBufferSize - m_iRIdx;
			if (len1 >= readLen)
			{
				memcpy(pBuf, &m_pBuffer[m_iRIdx], readLen);
				m_iRIdx += readLen;
			}
			else
			{
				memcpy(pBuf, &m_pBuffer[m_iRIdx], len1);
				memcpy(pBuf + len1, m_pBuffer, readLen - len1);
				m_iRIdx = readLen - len1;
			}
		} //end m_iRIdx >= m_iWidx

		return readLen;
	}//end bufferDataLen > readLen
}

// read to widx
int RingBuffer::ReadToWidx(unsigned char* pBuf, int iWidx)
{
	//m_pBuffer may alloc memory failed
	if (!m_pBuffer)
	{
		return -1;
	}
	if (!pBuf || m_iWidx == m_iRIdx)
	{
		return -1;
	}

	int curWidx = m_iWidx;
	if (m_iRIdx < curWidx)
	{
		if (iWidx < m_iRIdx || iWidx > curWidx)
		{
			return -1;
		}
	}
	else
	{
		if (iWidx > curWidx && iWidx < m_iRIdx)
		{
			return -1;
		}
	}

	//must use temp varible here
	//int iWidx = m_iWidx;
	int readLen = 0;
	if (m_iRIdx > iWidx)
	{
		memcpy(pBuf, &m_pBuffer[m_iRIdx], m_uBufferSize - m_iRIdx);
		memcpy(pBuf + m_uBufferSize - m_iRIdx, m_pBuffer, iWidx);
		readLen = m_uBufferSize - m_iRIdx + iWidx;
	}
	else
	{
		memcpy(pBuf, &m_pBuffer[m_iRIdx], iWidx - m_iRIdx);
		readLen = iWidx - m_iRIdx;
	}
	//###can not set m_iRIdx = m_iWidx!!!!!
	m_iRIdx = iWidx;

	return readLen;
}

int RingBuffer::ReadAll(unsigned char* pBuf)
{
	//m_pBuffer may alloc memory failed
	if (!m_pBuffer)
	{
		return -1;
	}
	if (!pBuf || m_iWidx == m_iRIdx)
	{
		return -1;
	}

	return ReadToWidx(pBuf, m_iWidx);
}
关键函数就这些了,有些地方写的比较繁琐,因为已经测试通过了,当做工具类,暂时就不改了...
 cpp下载地址: http://download.csdn.net/detail/lifexx/9603842