我正在实现一个功能最少的concurrent_blocking_queue
:
//a thin wrapper over std::queue
template<typename T>
class concurrent_blocking_queue
{
std::queue<T> m_internal_queue;
//...
public:
void add(T const & item);
T& remove();
bool empty();
};
我打算将它用于producer-consumer problem(我想,这是使用这种数据结构的地方吗?)。但我坚持一个问题是:
生产者完成后如何优雅地通知消费者?生产者完成后如何通知队列?通过调用特定的成员函数,说done()
?从队列中抛出异常(即来自remove
函数)是一个好主意吗?
我遇到了很多例子,但都有无限循环,好像生产者将永远生产物品。没有讨论停止条件的问题,甚至没有讨论wiki article。
答案 0 :(得分:5)
我过去只是介绍了一个虚拟的“完成”产品。因此,如果生产者可以创建类型A和类型B的“产品”,我发明了“完成”类型。当消费者遇到“完成”类型的产品时,它知道不再需要进一步处理。
答案 1 :(得分:2)
确实,将一个特殊的&#34;我们已经完成&#34;排队等等是很常见的。信息;但我认为OP对带外指标的原始愿望是合理的。看看人们正在考虑设置带内完成消息的复杂性!代理类型,模板;好悲伤。我说done()
方法更简单,更简单,它使常见的情况(我们还没有完成)更快更清洁。
我同意kids_fox的说法,如果队列完成则返回错误代码的try_remove是首选,但那样是风格和YMMV。
编辑: 用于实现队列的奖励积分,该队列记录多生产者情况下剩余的生产者数量,并且如果所有生产者已经抛弃,则引发完成的异常;-)不会执行 带有带内消息!
答案 2 :(得分:1)
'停止'并不经常讨论,因为它经常从未完成。在需要它的情况下,使用更高级别的P-C协议本身将毒丸排队更容易和更灵活,因为它是在队列本身中构建额外的功能。
如果你真的想要这样做,你确实可以设置一个标志,让每个消费者引发异常,“立即”或每当它回到队列时,但是有问题。你需要'done'方法是同步的,即。你想让所有的消费者在'完成'回归时消失,或者异步,即。当所有其他消费者都消失时,最后一个消费者线程调用一个事件参数?
您如何安排那些正在等待醒来的消费者?有多少人正在等待,有多少人在忙,但是当他们完成工作后会回到队列中?如果一个或多个消费者陷入阻塞呼叫,(或许他们可以解锁,但这需要来自另一个线程的呼叫 - 你打算怎么做)怎么办?
消费者如何通知他们已处理异常并即将死亡?是“即将死”,还是需要等待线程处理?如果你必须等待线程句柄,那么等待什么 - 请求队列关闭的线程或最后一个消费者线程要通知?
哦,是的 - 为了安全起见,您应该安排生产者线程,这些线程会在“关闭”状态下与对象一起排队,以便引发异常。
我提出这些问题,因为我很久以前就完成了这一切。最终,这一切都奏效了。排队的对象都必须在其继承链中插入“QueuedItem”(以便可以将作业取消方法暴露给队列),并且队列必须保留一个线程安全的对象列表。弹出 线程但尚未处理。
过了一会儿,我停止使用该类,而不使用特殊的关闭机制的简单P-C队列。
答案 3 :(得分:1)
我的队列通常使用指针(在{。}}中使用std::auto_ptr
界面,以清楚地表明发件人可能不再访问
指针);在大多数情况下,排队的对象是多态的,所以
无论如何都需要动态分配和引用语义。
否则,添加“结尾”应该不会太难
file“标志到队列。你需要一个特殊的功能
生产者方(close
?)来设置它(使用完全相同的锁定
当你写入队列时的原语)和删除中的循环
函数必须等待存在的东西或队列
关闭。当然,您需要返回Fallible
值,这样
读者可以知道读取是否成功。另外,不要
忘记在这种情况下,你需要一个notify_all
来确保所有
等待条件的进程被唤醒。
BTW:我不太清楚你的界面是如何实现的。是什么
T&
返回的remove
是指。基本上,remove
必须是。{1}}
类似的东西:
Fallible<T>
MessageQueue<T>::receive()
{
ScopedLock l( myMutex );
while ( myQueue.empty() && ! myIsDone )
myCondition.wait( myMutex );
Fallible<T> results;
if ( !myQueue.empty() ) {
results.validate( myQueue.top() );
myQueue.pop();
}
return results;
}
即使没有myIsDone
条件,您也必须将值读入
局部变量在从队列中删除之前,你不能返回一个
引用局部变量。
其余的:
void
MessageQueue<T>::send( T const& newValue )
{
ScopedLock l( myMutex );
myQueue.push( newValue );
myCondition.notify_all();
}
void
MessageQueue<T>::close()
{
ScopedLock l( myMutex );
myIsDone = true;
myCondition.notify_all();
}