我之前没有使用过并发队列。
在while循环中可以使用如下的TryDequeue吗?这不会永远陷入困境吗?
var cq = new ConcurrentQueue<string>();
cq.Enqueue("test");
string retValue;
while(!cq.TryDequeue(out retValue))
{
// Maybe sleep?
}
//Do rest of code
答案 0 :(得分:13)
在某种意义上说它是安全的,直到它已经拉出一个项目,并且如果队列中有一个项目被取出,它将最终结束。如果队列被另一个线程清空,并且不再添加任何项目,那么循环当然不会结束。
除此之外,你所拥有的是一个繁忙的循环。实际上应该始终避免这种情况。要么你不断轮询队列要求更多的项目,浪费CPU时间和精力,或者你最终睡觉,因此一旦添加它就不会实际使用队列中的项目(即便如此,仍然浪费在上下文切换上的一些时间/精力只是为了轮询队列。)
如果你发现自己处于想要“等到我有一件物品可以拿走”的位置,那么你应该做的是使用BlockingCollection
。 具体旨在包装各种类型的并发集合并阻塞,直到有可用的项目为止。它允许您将代码更改为queue.Take()
并使其更容易编写,从语义上说明您正在做什么,明确正确,明显更有效,以及完全安全。
答案 1 :(得分:11)
是的,根据文档是安全的,但它不是推荐的设计。
它可能会被永久地困住&#34;如果在第一次调用TryDequeue时队列是空的,并且如果在该点之后没有其他线程在队列中推送数据(你可以在N次尝试之后或在超时之后中断)。
ConcurrentQueue提供IsEmpty成员来检查队列中是否有项目。检查它比循环TryDequeue调用更有效(特别是如果队列通常为空)
可能想要做的是:
while(cq.IsEmpty())
{
// Maybe sleep / wait / ...
}
if(cq.TryDequeue(out retValue))
{
...
}
编辑: 如果最后一次调用返回false:你的线程中的另一个将该项目队列化。如果你没有其他线程,这是安全的,如果你这样做,你应该使用while(TryDequeue)