这是在c++中实现有界缓冲区的正确方法吗?
Is this a correct way to implement a bounded buffer in C++
我正在编写一个程序,该程序处理多个线程访问、存入和从有界缓冲区容器中取出的情况。我注意到线程的一些主要问题,并怀疑我的缓冲区在某个地方部分或根本不正确。
为了确保我知道我在做什么,我希望有我的缓冲区代码检查。该类使用我在其他地方实现的信号量,我假设它现在可以工作(如果不行,我很快就会弄清楚!)我补充了一些评论,试图解释我的推理。
首先,.h文件:#ifndef BOUNDED_BUFFER_H
#define BOUNDED_BUFFER_H
#include "Semaphore.H"
#include <string>
#include <vector>
using namespace std;
class Item{ //supposed to eventually be more extensible...seems silly with just a string for now
public:
Item(string _content){content = _content;}
string GetContent() {return content;}
private:
};
class BoundedBuffer{
public:
BoundedBuffer();
void Deposit(Item* _item);
Item* Retrieve();
int GetNumItems() {return count;}
vector<Item*> GetBuffer() {return buffer;}
void SetSize(int _size){
capacity = _size;
buffer.reserve(_size); //or do I need to call "resize"
}
private:
int capacity;
vector<Item*> buffer; //I originally wanted to use an array but had trouble with
//initilization/sizing, etc.
int nextin;
int nextout;
int count;
Semaphore notfull; //wait on this one before depositing an item
Semaphore notempty; //wait on this one before retrieving an item
};
#endif
接下来,.cpp:
#include "BoundedBuffer.H"
#include <iostream>
using namespace std;
BoundedBuffer::BoundedBuffer(){
notfull.SetValue(0);
notempty.SetValue(0);
nextin = 0;
nextout = 0;
count = 0;
}
void BoundedBuffer::Deposit(Item* _item){
if(count == capacity){
notfull.P(); //Cannot deposit into full buffer; wait
}
buffer[nextin] = _item;
nextin = (nextin + 1) % capacity; //wrap-around
count += 1;
notempty.V(); //Signal that retrieval is safe
}
Item* BoundedBuffer::Retrieve(){
if(count == 0){
notempty.P(); //cannot take from empty buffer; wait
}
Item* x = buffer[nextout];
nextout = (nextout + 1) % capacity;
buffer.pop_back(); //or a different erase methodology?
count -= 1;
notfull.V(); //Signal that deposit is safe
return x;
}
我认为问题可能是由于我选择向量作为底层容器(或者更确切地说,是对它的不正确使用),或者可能是需要更多的安全阻塞机制(互斥锁等?)从情况来看,有人能提供一些反馈吗?
这是一个非常常见的问题(关于如何做一个合适的多线程队列)。我以前见过的最好的答案是这个堆栈溢出问题和这个网站。这些答案适用于无界队列,因此我将在这里展开并展示一个适用于有界队列的答案。
您需要用互斥锁保护您的存款和检索函数,并使用条件变量来进行唤醒。
#include <mutex>
#include <condition_variable>
std::mutex the_mutex;
std::condition_variable the_notfull_cvar;
std::condition_variable the_notempty_cvar;
...
void BoundedBuffer::Deposit(Item* _item){
std::unique_lock<std::mutex> lock(the_mutex);
while ( /* buffer is full */ ){
/* simultaneously wait and release the mutex */
the_notfull_cvar.wait(lock);
/* the mutex is reaquired at this point */
}
/* buffer has space and we own the mutex: insert the item */
...
/* tell anyone waiting on an empty buffer that they can wake up. */
the_notempty_cvar.notify_all();
}
Item* BoundedBuffer::Retrieve(){
std::unique_lock<std::mutex> lock(the_mutex);
while ( /* buffer is empty */ ){
/* simultaneously wait and release the mutex */
the_notempty_cvar.wait(lock);
/* the mutex is reaquired at this point */
}
/* buffer has something in it and we own the mutex: get the item */
...
/* tell anyone waiting on a full buffer that they can wake up. */
the_notfull_cvar.notify_all();
return x;
}
你的GetNumItems(), GetBuffer()和SetSize()函数也需要用unique_locks保护。
你看过Boost:Circular_buffer吗?这是一个固定大小的存储场所,具有标准库接口。这可能是你想要的,或者给你一些提示。如果在循环缓冲区已满时写入内容,则循环缓冲区将覆盖开始部分。也许你不希望这样,尽管有一个完整的()测试,所以你可以避免在你的代码。
相关文章:
- 找到一种有效的方法,在 2 个巨大的缓冲区上执行 MAX,每字节字节
- 为什么 Glib::VariantBase::store 方法破坏了给定缓冲区的开始
- C++ - 有什么方法可以将输入插入缓冲区/停止 cin.忽略需要输入?
- OpenGL 顶点缓冲区类重定义和模板方法错误
- 将随机数放入缓冲区以写入文件的有效方法是什么?
- 将数组与传入的字节*缓冲区进行比较的最快方法
- memcmp - 是否有更快的方法来按位比较两个缓冲区
- 操作方法:将 boost::endian 缓冲区类型转换回本机格式
- 从二进制缓冲区初始化变量的正确方法是什么
- 从 char* 缓冲区读取int32_t的惯用 cpp14 方法是什么?
- 在环形缓冲区中向后循环的好方法是什么
- 是否有更好的方法来处理缓冲区和阅读中的不完整数据
- 在amd64体系结构上的C++中,将图像缓冲区blit到另一个缓冲区的xy偏移中的最快方法
- 从与对象大小不一致的连续固定大小缓冲区解析对象的有效方法
- 在堆栈和堆上使用较低级别的方法获取缓冲区的长度
- 在 c++ 中使用消息结构读取/填充数据缓冲区的正确方法是什么?
- 连接多个缓冲区的有效方法
- 在C++中使用 fread 时实现固定大小缓冲区的最佳方法是什么?
- VisualC++DLL将2-3兆字节复制到C#缓冲区的最快方法是什么
- 在字符串附加函数中,null终止无符号字符缓冲区的有效方法