22

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

在while循环中使用如下TryDequeue可以吗?这不能永远卡住吗?

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

string retValue;

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

//Do rest of code
4

2 回答 2

18

从某种意义上说,循环不会真正结束,直到它已经拉出一个项目,并且如果队列有一个要取出的项目最终会结束,这是安全的。如果队列被另一个线程清空并且没有添加更多项目,那么循环当然不会结束。

除此之外,您所拥有的是一个繁忙的循环。这实际上应该始终避免。要么您最终不断轮询队列以请求更多项目,从而在进程中浪费 CPU 时间和精力,要么您最终进入睡眠状态,因此一旦添加队列中的项目就没有真正使用它(即使那样,仍然在浪费上下文切换的一些时间/精力只是为了轮询队列)。

相反,如果您发现自己处于想要“等到有一件物品让我拿走”的位置,那么您应该做的是使用BlockingCollection. 它专门设计用于包装各种类型的并发集合并阻塞,直到有可用的项目。它使您可以更改代码queue.Take()并使其更易于编写,语义上说明您正在做的事情,清楚地正确,明显更有效且完全安全。

于 2014-05-23T14:50:57.713 回答
16

是的,根据文档,它是安全的,但不是推荐的设计。

如果在第一次调用 TryDequeue 时队列为空,并且在该点之后没有其他线程将数据推送到队列中,它可能会“永远卡住”(不过,您可以在 N 次尝试或超时后中断 while)。

ConcurrentQueue 提供了一个IsEmpty成员来检查队列中是否有项目。检查它比遍历 TryDequeue 调用更有效(特别是如果队列通常是空的)

可能想要做的是:

while(cq.IsEmpty())
{
    // Maybe sleep / wait / ...
}

if(cq.TryDequeue(out retValue))
{
...
}

编辑:如果最后一次调用返回 false:的另一个线程将该项目出队。如果你没有其他线程,这是安全的,如果你有,你应该使用 while(TryDequeue)

于 2014-05-23T14:35:38.583 回答