赞
踩
实际开发中,数据缓存区的概念是必须了解的。如经典的生产、消费模型就是维持着一块数据缓存区供相关的读写操作,当然线程安全问题跑不掉。这里记录下工作中经常使用且经过长时间检验的缓冲区代码,绝大部分场景完全满足,复制直接可用。
还有比较经典的环形缓存等设计方法,基本原理比较相似,但很难有万能、全面的方法设计,能兼顾稳定性、简洁性就是好的设计,应用场景不同,取长补短就好。有一点注意的是,任何缓冲区的读写速度都不应该相差太大,比如写的很快,读的很慢,那么必然缓存区需要很大(即使能自动扩充大小),这种是业务逻辑设计要做的事(避免或降低二者速度差距),完全让缓存区来解决是不合理的,生产和消费能力动态平衡是比较理想的状态。
此缓冲区的设计思路,如下图。此缓冲区特点是:
同样缺点是:
直接上代码,看代码理解就好,并不是很难,已经封装成了类。
#ifndef FIFOBUFFER_H #define FIFOBUFFER_H #include <list> #include <mutex> //数据节点 struct DataNode { DataNode() { begin = 0; end = 0; } //记录当前数据块的始、末位置 int begin; int end; }; //缓冲区类 实现队列存储数据块 只拷贝一次,提供给外部使用 class FiFoBuffer { public: FiFoBuffer(); ~FiFoBuffer(); //初始化缓冲区 bool initFiFoBuffer(int bufferLength); //释放缓冲区 void freeFiFoBuffer(); //重置缓冲区(不需要重新申请内存) void resetFiFoBuffer(); //获取当前的数据块节点数量 int getDataNodeSize(); //获取缓冲区总长度 int getFiFoBufferLength(); //获取缓冲区剩余空间大小 int getRemainSapce(); //数据写入缓冲区尾部 注:缓存区满则写入失败 bool pushData(char *data, int length); //返回缓冲区头部数据及长度供外部使用 需要popDelete来释放此数据块空间 char *popData(int *length); //调用此函数删除头部数据块 bool popDelete(); private: //线程安全锁 std::mutex fifoMutex; //缓冲区相关变量 char *fifoBuffer; int fifoBufferLength; int fifoBegin; int fifoEnd; //数据块节点 std::list<DataNode> dataNodeList; }; #endif // FIFOBUFFER_H
#include "fifobuffer.h" FiFoBuffer::FiFoBuffer() { fifoBuffer = nullptr; fifoBufferLength = 0; fifoBegin = 0; fifoEnd = 0; } FiFoBuffer::~FiFoBuffer() { freeFiFoBuffer(); } bool FiFoBuffer::initFiFoBuffer(int bufferLength) { if(bufferLength < 0) return false; std::lock_guard<std::mutex> lock(fifoMutex); if (fifoBuffer != nullptr) return false; fifoBuffer = new char[bufferLength]; if(fifoBuffer == nullptr) return false; fifoBufferLength = bufferLength; memset(fifoBuffer, 0, fifoBufferLength); fifoBegin = 0; fifoEnd = 0; dataNodeList.clear(); return true; } void FiFoBuffer::freeFiFoBuffer() { std::lock_guard<std::mutex> lock(fifoMutex); fifoBegin = 0; fifoEnd = 0; fifoBufferLength = 0; dataNodeList.clear(); if (fifoBuffer != nullptr) { delete[] fifoBuffer; fifoBuffer = NULL; } } void FiFoBuffer::resetFiFoBuffer() { std::lock_guard<std::mutex> lock(fifoMutex); fifoBegin = 0; fifoEnd = 0; dataNodeList.clear(); memset(fifoBuffer, 0, fifoBufferLength); } int FiFoBuffer::getDataNodeSize() { std::lock_guard<std::mutex> lock(fifoMutex); return dataNodeList.size(); } int FiFoBuffer::getFiFoBufferLength() { std::lock_guard<std::mutex> lock(fifoMutex); return fifoBufferLength; } int FiFoBuffer::getRemainSapce() { std::lock_guard<std::mutex> lock(fifoMutex); if (fifoBufferLength <= 0) return fifoBufferLength; if (dataNodeList.size() <= 0) return fifoBufferLength; int listLengthCount = 0; std::list<DataNode>::iterator it; for (it = dataNodeList.begin(); it != dataNodeList.end(); ++it) { DataNode node = *it; listLengthCount += node.end - node.begin; } return fifoBufferLength - listLengthCount; } bool FiFoBuffer::pushData(char *data, int length) { std::lock_guard<std::mutex> lock(fifoMutex); //条件判断 if (fifoBufferLength <= 0 || fifoBuffer == NULL || data == NULL || length <= 0 || length > fifoBufferLength) return false; //检测剩余的空间是否够存储 不够则重头开始存储 if (fifoBufferLength - fifoEnd < length) fifoEnd = 0; if (dataNodeList.size() > 0) { int nStart = 0; DataNode nodeFront = dataNodeList.front(); nStart = nodeFront.begin; if (fifoEnd == 0) { //是否可以重头开始 if (nStart < length) return false; } else { if (nStart >= fifoEnd) { //剩余空间不够 if ((nStart - fifoEnd) < length) return false; } else { //剩余空间不够 if (fifoBufferLength - fifoEnd < length) return false; } } } //记录新的数据块 DataNode node; node.begin = fifoEnd; node.end = node.begin + length; dataNodeList.push_back(node); memcpy(fifoBuffer + fifoEnd, data, length); fifoEnd += length; return true; } char *FiFoBuffer::popData(int *length) { std::lock_guard<std::mutex> lock(fifoMutex); char *buffer = nullptr; if (dataNodeList.size() <= 0) { *length = 0; return buffer; } DataNode node; node = dataNodeList.front(); //为了稳定性再次判断返回的节点是否有效 int offset = node.end - node.begin; if (node.begin >= 0 && node.end > 0 && node.begin < fifoBufferLength && node.end <= fifoBufferLength && offset > 0 && offset <= fifoBufferLength) { buffer = fifoBuffer + node.begin; *length = offset; return buffer; } else { buffer = NULL; *length = 0; return buffer; } } bool FiFoBuffer::popDelete() { std::lock_guard<std::mutex> lock(fifoMutex); if (dataNodeList.size() <= 0) return false; //删除数据块记录即可,表示该数据块区域可被再次使用 dataNodeList.pop_front(); if (dataNodeList.size() <= 0) { fifoBegin = 0; fifoEnd = 0; } return true; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。