将TryDequeue放入while循环中是否安全?

时间:2014-05-23 14:05:33

标签: c# task-parallel-library concurrent-queue

我之前没有使用过并发队列。

在while循环中可以使用如下的TryDequeue吗?这不会永远陷入困境吗?

var cq = new ConcurrentQueue<string>();
cq.Enqueue("test");

string retValue;

while(!cq.TryDequeue(out retValue))
{
    // Maybe sleep?
}

//Do rest of code

2 个答案:

答案 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)