并发队列使用

问题描述 投票:0回答:2

有一个简单的问题。

如果一个线程正在排队而另一个线程正在出队,我是否必须使用并发队列?在这种情况下使用常规容器(1 个读取器和 1 个写入器)是否存在任何竞争条件/其他风险?

c# concurrency thread-safety race-condition concurrent-queue
2个回答
5
投票

使用

ConcurrentQueue<T>
,您可以安全地从多个线程并行调用方法
Enqueue
TryDequeue
。这里没有竞争条件。您可以全天每秒执行 1,000,000 次,没有任何问题(假设您在执行此操作时不会消耗所有可用内存)。如果您想等待某个项目变得可用(如果没有),则可能存在竞争条件。例如,消费者线程可以像这样循环运行:

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<T>
类来解决。

foreach (var item in blockingCollection.GetConsumingEnumerable())
{
    Process(item);
}

GetConsumingEnumerable
方法可确保即时通知新项目或收集完成。

BlockingCollection<T>
类有一个缺点。顾名思义,它在等待期间阻塞当前线程。如果您想避免这种情况,您可以在此处查看异步替代方案的快速摘要。


0
投票

您不必这样做,您可以使用非并发收集,但您必须使用显式锁同步线程以使收集并发。

如果使用得当,并发收集将会更快、更高效,而不需要实现手动锁。

© www.soinside.com 2019 - 2024. All rights reserved.