在什么条件下TryDequeue和类似的System.Collections.Concurrent集合方法都会失败

Max*_*ich 11 c# collections parallel-processing concurrency multithreading

我最近注意到,在System.Collections.Concurrent命名空间中包含的集合对象中,通常会看到Collection.TrySomeAction()而不是Collection.SomeAction().

这是什么原因?我认为它与锁定有关?

所以我想知道在什么条件下尝试(例如)从堆栈,队列,包等中取出项目..失败?

Nic*_*nko 10

System.Collections.Concurrent命名空间中的集合被认为是线程安全的,因此可以使用它们来编写在线程之间共享数据的多线程程序.

在.NET 4之前,如果多个线程可能正在访问单个共享集合,则必须提供自己的同步机制.每次修改元素时都必须锁定集合.每次访问(或枚举)它时,您也可能需要锁定集合.这是最简单的多线程场景.某些应用程序会创建后台线程,随着时间的推移将结果传递给共享集合.另一个线程将读取并处理这些结果.您需要在线程之间实现自己的消息传递方案,以便在新结果可用时以及消耗这些新结果时相互通知.类和接口System.Collections.Concurrent为那些以及其他常见的多线程编程问题提供了一致的实现,这些问题涉及以无方式跨线程共享数据.

Try<something>具有语义 - 尝试执行该操作并返回操作结果.DoThat语义通常使用异常抛出的机制来指示可能无效的错误.作为例子,他们可以返回false,

  • 如果你尝试添加新的元素,你可能已经拥有它ConcurentDictionary;
  • 如果你试图从集合中获取元素,那么它可能不存在 ;
  • 如果您尝试更新元素,则可能已有更新的元素,因此方法确保它仅更新要更新的元素.

试着读:


Fre*_*örk 9

失败是什么意思?

请看以下示例:

var queue = new Queue<string>();
string temp = queue.Dequeue();
// do something with temp
Run Code Online (Sandbox Code Playgroud)

上面的代码抛出异常,因为我们试图从空队列中出队.现在,如果你ConcurrentQueue<T>改用:

var queue = new ConcurrentQueue<string>();
string temp;
if (queue.TryDequeue(out temp))
{
    // do something with temp
}
Run Code Online (Sandbox Code Playgroud)

上面的代码不会抛出异常.队列仍然无法使项目出列,但代码不会以抛出异常的方式失败.在多线程环境中,这一点的实际用途变得明显.非并发代码Queue<T>通常如下所示:

lock (queueLock)
{
    if (queue.Count > 0)
    {
        string temp = queue.Dequeue();
        // do something with temp   
    } 
}
Run Code Online (Sandbox Code Playgroud)

为了避免竞争条件,我们需要使用一个锁来确保在检查Countdo调用时从队列中没有任何反应Dequeue.有了ConcurrentQueue<T>,我们真的不需要检查Count,但可以改为打电话TryDequeue.

如果检查中发现的类型Systems.Collections.Concurrent的命名空间,你会发现他们中许多人换两个操作通常被称为顺序,而传统上会需要锁定(Count其次是DequeueConcurrentQueue<T>,GetOrAddConcurrentDictionary<TKey, TValue>替代调用的顺序ContainsKey,增加一个项目,并得到它,并等等).