+-
c++缓冲池循环队列实现
首页 专栏 c++ 文章详情
0

c++缓冲池循环队列实现

鲸冬香 发布于 5 月 15 日

承接上周任务,目的效果是写线程将数据写入缓冲区,读线程获取缓冲区中的数据。

缓冲区

数据结构

所谓缓冲区,就是开辟一段内存空间来保存数据,主要包括的属性为储存数据的内存空间,缓冲区长度,已使用的长度。对应的方法为将数据写入缓冲区,从缓冲区中读入数据,设置已写入的缓冲区长度。所建立的数据结构为:

class Buffer { private: USHORT* buffer; // 缓冲区 int maxSize; // 缓冲区最大长度 int effectiveSize; // 已经使用长度 public: Buffer(int bufferSize); // 设置缓冲区大小 void setEffectiveSize(int size); // 设置缓冲区已用数据 void write(std::function<int(USHORT*, int)> const& writeBuffer); // 将数据写入缓冲区 void read(std::function<void(USHORT*, int, int)> const& readBuffer); // 从缓冲区中读取数据 ~Buffer(); };

这里write方法和read方法接受的参数为c++11定义的lambda表达式。例std::function<int(USHORT*, int)> const& writeBuffer代表传入一个lambda表达式,接受参数为USHORT*, int

具体实现

构造函数实现:
将缓冲区大小作为参数传递给构造函数,在构造函数中申请内存空间,并设置相应属性。 writeread函数实现
将缓冲区数据和缓冲区长度作为参数传入 lambda表达式参数并调用。 Buffer::Buffer(int size) { this->maxSize = size; this->effectiveSize = 0; this->buffer = new USHORT[size]; } void Buffer::setEffectiveSize(int size) { this->effectiveSize = size; } /** * writeBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区最大长度, 返回int:写入数据的长度 */ void Buffer::write(std::function<int(USHORT*, int)> const& writeBuffer) { this->effectiveSize = writeBuffer(this->buffer, this->maxSize); } /** * readBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区有效长度 int:缓冲区最大长度 返回void */ void Buffer::read(std::function<void(USHORT*, int, int)> const& readBuffer) { readBuffer(this->buffer, this->effectiveSize, this->maxSize); }

对于同一个缓冲区,writeread操作因该是互斥的,否则就会导致数据错乱。因此需要信号量的实现来保证writeread互斥。

信号量实现

关于c++信号量可参考这篇文章:C++ 并发编程(六):信号量(Semaphore)

class Semaphore { private: std::mutex mutex; // 互斥量 std::condition_variable cv; // 条件变量 int count; // 可用资源数 public: Semaphore(int count = 0); void singal(); // 释放一个资源 void wait(); // 等待一个资源 }; Semaphore::Semaphore(int count) { if (count < 0) { throw "可用资源不能小于0"; } this->count = count; } /** * 释放资源 */ void Semaphore::singal() { std::unique_lock<std::mutex> lock(this->mutex); ++this->count; this->cv.notify_one(); } /** * 申请资源 */ void Semaphore::wait() { std::unique_lock<std::mutex> lock(this->mutex); this->cv.wait(lock, [=] { return count > 0; }); // reutrn true时往下执行 --this->count; }

完善缓冲区

在缓冲区数据结构中加入信号量:

class Buffer { private: ...... Semaphore* sem; // 使用信号量保证缓冲区使用互斥 };

writeread方法中使用信号量实现互斥。

Buffer::Buffer(int size) { ....... this->sem = new Semaphore(1); } /** * 将数据写入缓冲区,互斥操作 * writeBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区最大长度, 返回int:写入数据的长度 */ void Buffer::write(std::function<int(USHORT*, int)> const& writeBuffer) { this->sem->wait(); this->effectiveSize = writeBuffer(this->buffer, this->maxSize); this->sem->singal(); } /** * 读取缓冲区数据,互斥操作 * readBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区有效长度 int:缓冲区最大长度 返回void */ void Buffer::read(std::function<void(USHORT*, int, int)> const& readBuffer) { this->sem->wait(); readBuffer(this->buffer, this->effectiveSize, this->maxSize); this->sem->singal(); }

缓冲池

缓冲池实际上就是缓冲区的集合。通过缓冲池来分配缓冲区。

数据结构

/** * 缓冲池定义,存储并分配缓冲区 */ class BufferPool { private: int head, tail; // 头尾指针 Buffer** buffers; // 缓冲池 int total, lenth; // 缓冲区总个数和以使用个数 public: BufferPool(int count = 10, int bufferSize = DEFAULT_BUFFER_SIZE); // 构造函数 count:缓冲区个数 bufferSize:缓冲区大小 Buffer* getBuffer(); // 获取一个缓冲区 Buffer* popBuffer(); // 取得头缓冲区并弹出 bool empty(); // 缓冲池是否为空 bool full(); // 缓冲池是否已满 ~BufferPool(); };

具体实现

要保证按数据写入的顺序读出,应该把缓冲池设计为队列,保证读取时缓冲区总是最先写入的,同时写入时获得的缓冲区是最末尾的缓冲区。同时为了保证缓冲区循环利用,将缓冲池设计为循环队列。

对于循环队列,当头指针和尾指针相等时,有两种情况。一种是队列为空(未分配缓冲区),另一种是队列已满(所有缓冲区都被分配)。解决方法一般有两种,第一种是牺牲一个存储空间,当尾指针指向的下一位为头指针时,即队列为满。另一种是增加标志位来判断当头尾指针相同时,当前队列的状态。

由于本项目一个缓冲区设置空间较大,所以采用第二种方法,增加lenth属性表示当前使用的缓冲区个数,用来判断队列为空或满。

BufferPool::BufferPool(int count, int bufferSize) { this->head = 0; this->tail = 0; this->lenth = 0; this->total = count; this->buffers = new Buffer*[count]; for (int i = 0; i < count; i ++) { this->buffers[i] = new Buffer(bufferSize); } } /** * 获取一个缓冲区 当缓冲池已满时,覆盖旧数据 */ Buffer* BufferPool::getBuffer() { Buffer* buffer = this->buffers[this->tail]; // tail指针指向下一个缓冲区,如果当前缓冲池已满,头指针下移 this->tail = (this->tail + 1) % this->total; this->lenth++; if (this->lenth > this->total) { this->head = (this->head + 1) % this->total; this->lenth = this->total; } return buffer; } /** * 获取头缓冲区并弹出 */ Buffer* BufferPool::popBuffer() { if (this->lenth == 0) { throw "缓冲池为空"; } Buffer* buffer = this->buffers[this->head]; this->head = (this->head + 1) % this->total; this->lenth--; return buffer; } BufferPool::~BufferPool() { for (int i = 0; i < this->total; i ++) { delete this->buffers[i]; this->buffers[i] = NULL; } delete this->buffers; this->buffers = NULL; } bool BufferPool::empty() { return this->lenth == 0; } bool BufferPool::full() { return this->lenth == this->total; }

测试

按照上次汇报,从缓冲池中获取缓冲区,并将数据写入缓冲区。

// 用缓冲池来保存数据 BufferPool* bufferPool = new BufferPool(); // 获取一个缓冲区并将AD数据写入 bufferPool->getBuffer()->write([&](USHORT* buffer, int maxSize) { if (!ACTS1000_ReadDeviceAD(hDevice, buffer, maxSize, &nRetSizeWords, &nAvailSampsPoints, 5.0)) // 采集数据,将数据保存到ADBuffer中,nRetSizeWords代表实际共读取了多少个点 { printf("ReadDeviceDmaAD error...\n"); _getch(); } return nRetSizeWords; });

获取缓冲区数据:

while (!bufferPool->empty()) { bufferPool->popBuffer()->read([&](USHORT* ADBuffer, int eff, int maxSize) { for (int Index = 0; Index < 2; Index++) { printf("%d:%hu", Index, ADBuffer[Index]); } }); }
c++11 c++ 缓冲区
阅读 46 发布于 5 月 15 日
举报
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
我的专栏
一周的报告
关注专栏
avatar
鲸冬香
431 声望
23 粉丝
关注作者
0 条评论
得票数 最新
提交评论
avatar
鲸冬香
431 声望
23 粉丝
关注作者
宣传栏
目录

承接上周任务,目的效果是写线程将数据写入缓冲区,读线程获取缓冲区中的数据。

缓冲区

数据结构

所谓缓冲区,就是开辟一段内存空间来保存数据,主要包括的属性为储存数据的内存空间,缓冲区长度,已使用的长度。对应的方法为将数据写入缓冲区,从缓冲区中读入数据,设置已写入的缓冲区长度。所建立的数据结构为:

class Buffer { private: USHORT* buffer; // 缓冲区 int maxSize; // 缓冲区最大长度 int effectiveSize; // 已经使用长度 public: Buffer(int bufferSize); // 设置缓冲区大小 void setEffectiveSize(int size); // 设置缓冲区已用数据 void write(std::function<int(USHORT*, int)> const& writeBuffer); // 将数据写入缓冲区 void read(std::function<void(USHORT*, int, int)> const& readBuffer); // 从缓冲区中读取数据 ~Buffer(); };

这里write方法和read方法接受的参数为c++11定义的lambda表达式。例std::function<int(USHORT*, int)> const& writeBuffer代表传入一个lambda表达式,接受参数为USHORT*, int

具体实现

构造函数实现:
将缓冲区大小作为参数传递给构造函数,在构造函数中申请内存空间,并设置相应属性。 writeread函数实现
将缓冲区数据和缓冲区长度作为参数传入 lambda表达式参数并调用。 Buffer::Buffer(int size) { this->maxSize = size; this->effectiveSize = 0; this->buffer = new USHORT[size]; } void Buffer::setEffectiveSize(int size) { this->effectiveSize = size; } /** * writeBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区最大长度, 返回int:写入数据的长度 */ void Buffer::write(std::function<int(USHORT*, int)> const& writeBuffer) { this->effectiveSize = writeBuffer(this->buffer, this->maxSize); } /** * readBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区有效长度 int:缓冲区最大长度 返回void */ void Buffer::read(std::function<void(USHORT*, int, int)> const& readBuffer) { readBuffer(this->buffer, this->effectiveSize, this->maxSize); }

对于同一个缓冲区,writeread操作因该是互斥的,否则就会导致数据错乱。因此需要信号量的实现来保证writeread互斥。

信号量实现

关于c++信号量可参考这篇文章:C++ 并发编程(六):信号量(Semaphore)

class Semaphore { private: std::mutex mutex; // 互斥量 std::condition_variable cv; // 条件变量 int count; // 可用资源数 public: Semaphore(int count = 0); void singal(); // 释放一个资源 void wait(); // 等待一个资源 }; Semaphore::Semaphore(int count) { if (count < 0) { throw "可用资源不能小于0"; } this->count = count; } /** * 释放资源 */ void Semaphore::singal() { std::unique_lock<std::mutex> lock(this->mutex); ++this->count; this->cv.notify_one(); } /** * 申请资源 */ void Semaphore::wait() { std::unique_lock<std::mutex> lock(this->mutex); this->cv.wait(lock, [=] { return count > 0; }); // reutrn true时往下执行 --this->count; }

完善缓冲区

在缓冲区数据结构中加入信号量:

class Buffer { private: ...... Semaphore* sem; // 使用信号量保证缓冲区使用互斥 };

writeread方法中使用信号量实现互斥。

Buffer::Buffer(int size) { ....... this->sem = new Semaphore(1); } /** * 将数据写入缓冲区,互斥操作 * writeBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区最大长度, 返回int:写入数据的长度 */ void Buffer::write(std::function<int(USHORT*, int)> const& writeBuffer) { this->sem->wait(); this->effectiveSize = writeBuffer(this->buffer, this->maxSize); this->sem->singal(); } /** * 读取缓冲区数据,互斥操作 * readBuffer: lambda表达式,接受参数为 USHORT*:缓冲区数据 int:缓冲区有效长度 int:缓冲区最大长度 返回void */ void Buffer::read(std::function<void(USHORT*, int, int)> const& readBuffer) { this->sem->wait(); readBuffer(this->buffer, this->effectiveSize, this->maxSize); this->sem->singal(); }

缓冲池

缓冲池实际上就是缓冲区的集合。通过缓冲池来分配缓冲区。

数据结构

/** * 缓冲池定义,存储并分配缓冲区 */ class BufferPool { private: int head, tail; // 头尾指针 Buffer** buffers; // 缓冲池 int total, lenth; // 缓冲区总个数和以使用个数 public: BufferPool(int count = 10, int bufferSize = DEFAULT_BUFFER_SIZE); // 构造函数 count:缓冲区个数 bufferSize:缓冲区大小 Buffer* getBuffer(); // 获取一个缓冲区 Buffer* popBuffer(); // 取得头缓冲区并弹出 bool empty(); // 缓冲池是否为空 bool full(); // 缓冲池是否已满 ~BufferPool(); };

具体实现

要保证按数据写入的顺序读出,应该把缓冲池设计为队列,保证读取时缓冲区总是最先写入的,同时写入时获得的缓冲区是最末尾的缓冲区。同时为了保证缓冲区循环利用,将缓冲池设计为循环队列。

对于循环队列,当头指针和尾指针相等时,有两种情况。一种是队列为空(未分配缓冲区),另一种是队列已满(所有缓冲区都被分配)。解决方法一般有两种,第一种是牺牲一个存储空间,当尾指针指向的下一位为头指针时,即队列为满。另一种是增加标志位来判断当头尾指针相同时,当前队列的状态。

由于本项目一个缓冲区设置空间较大,所以采用第二种方法,增加lenth属性表示当前使用的缓冲区个数,用来判断队列为空或满。

BufferPool::BufferPool(int count, int bufferSize) { this->head = 0; this->tail = 0; this->lenth = 0; this->total = count; this->buffers = new Buffer*[count]; for (int i = 0; i < count; i ++) { this->buffers[i] = new Buffer(bufferSize); } } /** * 获取一个缓冲区 当缓冲池已满时,覆盖旧数据 */ Buffer* BufferPool::getBuffer() { Buffer* buffer = this->buffers[this->tail]; // tail指针指向下一个缓冲区,如果当前缓冲池已满,头指针下移 this->tail = (this->tail + 1) % this->total; this->lenth++; if (this->lenth > this->total) { this->head = (this->head + 1) % this->total; this->lenth = this->total; } return buffer; } /** * 获取头缓冲区并弹出 */ Buffer* BufferPool::popBuffer() { if (this->lenth == 0) { throw "缓冲池为空"; } Buffer* buffer = this->buffers[this->head]; this->head = (this->head + 1) % this->total; this->lenth--; return buffer; } BufferPool::~BufferPool() { for (int i = 0; i < this->total; i ++) { delete this->buffers[i]; this->buffers[i] = NULL; } delete this->buffers; this->buffers = NULL; } bool BufferPool::empty() { return this->lenth == 0; } bool BufferPool::full() { return this->lenth == this->total; }

测试

按照上次汇报,从缓冲池中获取缓冲区,并将数据写入缓冲区。

// 用缓冲池来保存数据 BufferPool* bufferPool = new BufferPool(); // 获取一个缓冲区并将AD数据写入 bufferPool->getBuffer()->write([&](USHORT* buffer, int maxSize) { if (!ACTS1000_ReadDeviceAD(hDevice, buffer, maxSize, &nRetSizeWords, &nAvailSampsPoints, 5.0)) // 采集数据,将数据保存到ADBuffer中,nRetSizeWords代表实际共读取了多少个点 { printf("ReadDeviceDmaAD error...\n"); _getch(); } return nRetSizeWords; });

获取缓冲区数据:

while (!bufferPool->empty()) { bufferPool->popBuffer()->read([&](USHORT* ADBuffer, int eff, int maxSize) { for (int Index = 0; Index < 2; Index++) { printf("%d:%hu", Index, ADBuffer[Index]); } }); }