我有一个快速的问题。
如果一个线程在入队,另一个线程在出队,我是否必须使用并发队列?在这种情况下(1个读者和1个写者),使用常规容器是否存在竞态条件或其他风险?
我有一个快速的问题。
如果一个线程在入队,另一个线程在出队,我是否必须使用并发队列?在这种情况下(1个读者和1个写者),使用常规容器是否存在竞态条件或其他风险?
使用ConcurrentQueue
,您可以安全地并行从多个线程中调用Enqueue
和TryDequeue
方法。这里没有竞争状态。您可以一整天每秒执行100万次,没有任何问题(假设在此过程中不会消耗所有可用内存)。但如果您想等待一个项目变为可用,则可能会出现竞争条件(如果没有可用的项目)。例如,消费者线程可以像这样循环运行:
while (true)
{
if (!queue.IsEmpty)
{
queue.TryDequeue(out var item); // Race condition!
Process(item);
}
else
{
Thread.Sleep(50);
}
}
IsEmpty
和 TryDequeue
之间。队列可能会在此期间被另一个线程清空。通过删除 IsEmpty
检查,可以消除这个竞态条件:while (true)
{
if (queue.TryDequeue(out var item)) // Fixed
{
Process(item);
}
else
{
Thread.Sleep(50);
}
}
然而,这种方法效率低下。线程将会进行无效的循环,当有可用项时,它将在延迟后获取该项。同时请注意,队列没有办法通知线程已经完成,并且不会再有任何项。这两个问题都可以通过专用的BlockingCollection
类来解决。
foreach (var item in blockingCollection.GetConsumingEnumerable())
{
Process(item);
}
GetConsumingEnumerable
方法确保对于新项或集合完成的即时通知。
BlockingCollection
类有一个缺点。正如其名称所示,它会在等待期间阻塞当前线程。如果您想避免这种情况,可以在这里查看异步替代方案的快速摘要。
Peek
而不是Dequeue
。 - undefined