C++ 无锁环形缓冲区实现

        前段时间有个项目要实现一个基于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  

你可能感兴趣的:(C++)