线程安全的环形缓冲区实现
2023-09-27 14:29:32 时间
来源:http://blog.csdn.net/lezhiyong
应用背景:线程1将每次数量不一的音频采样点(PCM音频数据)写入环形缓冲区,线程2每次取固定数量采样点送音频编码器,线程1线程2在平均时间内的读写数据量相等。(倒入桶中的水量有时大有时小,但每次取一瓢喝:)
该环形缓冲区借鉴CoolPlayer音频播放器中的环形缓冲区代码实现,在读写操作函数中加了锁,允许多线程同时操作。CPs_CircleBuffer基于内存段的读写,比用模板实现的环形缓冲队列适用的数据类型更广些, CPs_CircleBuffer修改成C++中基于对象的实现,加上详细注释,m_csCircleBuffer锁变量为自用的lock类型(将CRITICAL_SECTION封装起来),调用lock()加锁,调用unlock()解锁。使用效果良好,分享出来。
CPs_CircleBuffer环形缓冲还不具备当待写数据量超出空余缓冲时自动分配内存的功能,这个将在后续进行优化。
CPs_CircleBuffer使用步骤:
CPs_CircleBuffer* m_pCircleBuffer; m_pCircleBuffer = new CPs_CircleBuffer(bufsize); 2、写 if (m_pCircleBuffer- GetFreeSize() CIC_READCHUNKSIZE) Sleep(20); continue; m_pCircleBuffer- Write(internetbuffer.lpvBuffer,internetbuffer.dwBufferLength); 3、读 m_pCircleBuffer- Read(pDestBuffer,iBytesToRead, piBytesRead); 4、其他调用 if(m_pCircleBuffer- IsComplete()) break; iUsedSpace =m_pCircleBuffer- GetUsedSize(); m_pCircleBuffer- SetComplete();
CPs_CircleBuffer(const unsigned int iBufferSize); ~CPs_CircleBuffer(); public: // Public functions void Uninitialise(); void Write(const void* pSourceBuffer, const unsigned int iNumBytes); bool Read(void* pDestBuffer, const size_t iBytesToRead, size_t* pbBytesRead); void Flush(); unsigned int GetUsedSize(); unsigned int GetFreeSize(); void SetComplete(); bool IsComplete(); private: unsigned char* m_pBuffer; unsigned int m_iBufferSize; unsigned int m_iReadCursor; unsigned int m_iWriteCursor; HANDLE m_evtDataAvailable; Vlock m_csCircleBuffer; bool m_bComplete;
m_iBufferSize = iBufferSize; m_pBuffer = (unsigned char*)malloc(iBufferSize); m_iReadCursor = 0; m_iWriteCursor = 0; m_bComplete = false; m_evtDataAvailable = CreateEvent(NULL, FALSE, FALSE, NULL); CPs_CircleBuffer::~CPs_CircleBuffer() Uninitialise(); // Public functions void CPs_CircleBuffer::Uninitialise()//没有必要public这个接口函数,long120817 CloseHandle(m_evtDataAvailable); free(m_pBuffer); //Write前一定要调用m_pCircleBuffer- GetFreeSize(),如果FreeSize不够需要等待,long120817 void CPs_CircleBuffer::Write(const void* _pSourceBuffer, const unsigned int _iNumBytes) unsigned int iBytesToWrite = _iNumBytes; unsigned char* pSourceReadCursor = (unsigned char*)_pSourceBuffer; //CP_ASSERT(iBytesToWrite = GetFreeSize());//修改为没有足够空间就返回,write前一定要加GetFreeSize判断,否则进入到这里相当于丢掉数据, // long120817 if (iBytesToWrite GetFreeSize()) { return; } _ASSERT(m_bComplete == false); m_csCircleBuffer.Lock(); if (m_iWriteCursor = m_iReadCursor) { // 0 m_iBufferSize // |-----------------|===========|--------------| // pR- pW- // 计算尾部可写空间iChunkSize,long120817 unsigned int iChunkSize = m_iBufferSize - m_iWriteCursor; if (iChunkSize iBytesToWrite) { iChunkSize = iBytesToWrite; } // Copy the data memcpy(m_pBuffer + m_iWriteCursor,pSourceReadCursor, iChunkSize); pSourceReadCursor += iChunkSize; iBytesToWrite -= iChunkSize; // 更新m_iWriteCursor m_iWriteCursor += iChunkSize; if (m_iWriteCursor = m_iBufferSize)//如果m_iWriteCursor已经到达末尾 m_iWriteCursor -= m_iBufferSize;//返回到起点0位置,long120817 } //剩余数据从Buffer起始位置开始写 if (iBytesToWrite) { memcpy(m_pBuffer + m_iWriteCursor,pSourceReadCursor, iBytesToWrite); m_iWriteCursor += iBytesToWrite; _ASSERT(m_iWriteCursor m_iBufferSize);//这个断言没什么意思,应该_ASSERT(m_iWriteCursor = m_iReadCursor);long20120817 } SetEvent(m_evtDataAvailable);//设置数据写好信号量 m_csCircleBuffer.UnLock(); bool CPs_CircleBuffer::Read(void* pDestBuffer, const size_t _iBytesToRead, size_t* pbBytesRead) size_t iBytesToRead = _iBytesToRead; size_t iBytesRead = 0; DWORD dwWaitResult; bool bComplete = false; while (iBytesToRead 0 bComplete == false) { dwWaitResult = WaitForSingleObject(m_evtDataAvailable, CIC_WAITTIMEOUT);//等待数据写好,long120817 if (dwWaitResult == WAIT_TIMEOUT) { //TRACE_INFO2("Circle buffer - did not fill in time!"); *pbBytesRead = iBytesRead; return FALSE;//等待超时则返回 } m_csCircleBuffer.Lock(); if (m_iReadCursor m_iWriteCursor) { // 0 m_iBufferSize // |=================|-----|===========================| // pW- pR- unsigned int iChunkSize = m_iBufferSize - m_iReadCursor; if (iChunkSize iBytesToRead) iChunkSize = (unsigned int)iBytesToRead; //读取操作 memcpy((unsigned char*)pDestBuffer + iBytesRead,m_pBuffer + m_iReadCursor,iChunkSize); iBytesRead += iChunkSize; iBytesToRead -= iChunkSize; m_iReadCursor += iChunkSize; if (m_iReadCursor = m_iBufferSize)//如果m_iReadCursor已经到达末尾 m_iReadCursor -= m_iBufferSize;//返回到起点0位置,long120817 } if (iBytesToRead m_iReadCursor m_iWriteCursor) { unsigned int iChunkSize = m_iWriteCursor - m_iReadCursor; if (iChunkSize iBytesToRead) iChunkSize = (unsigned int)iBytesToRead; //读取操作 memcpy((unsigned char*)pDestBuffer + iBytesRead,m_pBuffer + m_iReadCursor,iChunkSize); iBytesRead += iChunkSize; iBytesToRead -= iChunkSize; m_iReadCursor += iChunkSize; } //如果有更多的数据要写 if (m_iReadCursor == m_iWriteCursor) { if (m_bComplete)//跳出下一个while循环,该值通过SetComplete()设置,此逻辑什么意思?long120817 bComplete = true; } else//还有数据可以读,SetEvent,在下一个while循环开始可以不用再等待,long120817 SetEvent(m_evtDataAvailable); m_csCircleBuffer.UnLock(); } *pbBytesRead = iBytesRead; return bComplete ? false : true; // 0 m_iBufferSize // |------------------------------------------------| // pR // pW //读写指针归零 void CPs_CircleBuffer::Flush() m_csCircleBuffer.Lock(); m_iReadCursor = 0; m_iWriteCursor = 0; m_csCircleBuffer.UnLock(); //获取已经写的内存 unsigned int CPs_CircleBuffer::GetUsedSize() return m_iBufferSize - GetFreeSize(); unsigned int CPs_CircleBuffer::GetFreeSize() unsigned int iNumBytesFree; m_csCircleBuffer.Lock(); if (m_iWriteCursor m_iReadCursor) { // 0 m_iBufferSize // |=================|-----|===========================| // pW- pR- iNumBytesFree = (m_iReadCursor - 1) - m_iWriteCursor; } else if (m_iWriteCursor == m_iReadCursor) { iNumBytesFree = m_iBufferSize; } else { // 0 m_iBufferSize // |-----------------|=====|---------------------------| // pR- pW- iNumBytesFree = (m_iReadCursor - 1) + (m_iBufferSize - m_iWriteCursor); } m_csCircleBuffer.UnLock(); return iNumBytesFree; //该函数什么时候调用?long120817 void CPs_CircleBuffer::SetComplete() m_csCircleBuffer.Lock(); m_bComplete = true; SetEvent(m_evtDataAvailable); m_csCircleBuffer.UnLock();
#define V_MUTEX CRITICAL_SECTION //利用临界区实现的锁变量 #define V_MUTEX_INIT(m) InitializeCriticalSection(m) #define V_MUTEX_LOCK(m) EnterCriticalSection(m) #define V_MUTEX_UNLOCK(m) LeaveCriticalSection(m) #define V_MUTEX_DESTORY(m) DeleteCriticalSection(m) #else #define V_MUTEX pthread_mutex_t #define V_MUTEX_INIT(m) pthread_mutex_init(m,NULL) #define V_MUTEX_LOCK(m) pthread_mutex_Lock(m) #define V_MUTEX_UNLOCK(m) pthread_mutex_unLock(m) #define V_MUTEX_DESTORY(m) pthread_mutex_destroy(m) #endif class Vlock public: Vlock(void) { V_MUTEX_INIT( m_Lock); } ~Vlock(void) { V_MUTEX_DESTORY( m_Lock); } public: void Lock(){V_MUTEX_LOCK( m_Lock);} void UnLock(){V_MUTEX_UNLOCK( m_Lock);} private: V_MUTEX m_Lock;
环形缓冲区 环形缓冲区 是一段 先进先出 的循环缓冲区,有一定的大小,我们可以把它抽象理解为一块环形的内存。 我们使用环形缓冲区主要有两个原因; (1)当我们要存储大量数据时,我们的计算机只能处理先写入的数据,处理完毕释放数据后,后面的数据需要前移一位,大量的数据会频繁分配释放内存,从而导致很大的开销。使用环形缓冲区 可以减少内存分配继而减少系统的开销。 (2)如果我们频繁快速的持续向计算机输入数据,计算机可能执行某个进程不能及时的执行输入的数据,导致数据丢失。这时,我们可以将要输入的数据放入环形缓冲区内,计算机就不会造成数据丢失。
C# 快速释放内存的大数组 原文:C# 快速释放内存的大数组 本文告诉大家如何使用 Marshal 做出可以快速释放内存的大数组。 最近在做 3D ,需要不断申请一段大内存数组,然后就释放他,但是 C# 对于大内存不是立刻释放,所以就存在一定的性能问题。
linux内存分配方法总结【转】 转自:http://www.bkjia.com/Linuxjc/443717.html 内存映射结构:1.32位地址线寻址4G的内存空间,其中0-3G为用户程序所独有,3G-4G为内核占有。2.struct page:整个物理内存在初始化时,每个4kb页面生成一个对应的struct page结构,这个page结构就独一无二的代表这个物理内存页面,并存放在mem_map全局数组中。
相关文章
- 【VS开发】关于线程安全一些细节体会
- 附029.Kubernetes安全之网络策略
- Atmel率先推出超级安全的加密器件构建智能、联网、安全的系统
- Java 枚举实现单例模式,线程安全又优雅!
- 《UNIX环境高级编程》笔记--errno是否是线程安全的?
- 【安全测试】渗透测试神器BurpSuite环境搭建
- 2016最受瞩目的十一个顶级安全会议
- Struts2漏洞拉响网站安全红色警报以及把Struts2更新为最新版本Struts2.3.15.1步骤
- 《云安全原理与实践》——第2章 2.0云计算安全风险分析
- eclipse安装findbugs及Find-sec-bugs安全组件
- 线程安全和线程不安全
- 线程安全和线程不安全
- Servlet的多线程和线程安全
- java-并发-线程安全
- 转-Spring单例模式与线程安全
- 设计模式。双重检查单例(优化到极致完美),解决单例懒汉式的线程不安全
- swift 实现一个高效线程安全的Array
- C++11:基于std::queue和std::mutex构建一个线程安全的队列
- flask_sqlalchemy的session线程安全源码解读
- 什么叫线程安全?