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

时间:2011-04-13 06:40:10

标签: c# multithreading collections concurrency parallel-processing

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

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

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

3 个答案:

答案 0 :(得分:10)

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

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

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

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

尝试阅读:

答案 1 :(得分:9)

失败是什么意思?

采用以下示例:

var queue = new Queue<string>();
string temp = queue.Dequeue();
// do something with temp

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

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

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

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

为了避免竞争条件,我们需要使用锁来确保在检查Count调用Dequeue时经过队列没有任何反应。使用ConcurrentQueue<T>,我们实际上不需要检查Count,而是可以调用TryDequeue

如果检查在Systems.Collections.Concurrent命名空间中找到的类型,您会发现其中许多包含两个通常按顺序调用的操作,而传统上需要锁定(Count后跟{{1在} Dequeue中,ConcurrentQueue<T>中的GetOrAdd替换了调用ConcurrentDictionary<TKey, TValue>的序列,添加项目并获取它等等。

答案 2 :(得分:0)

如果没有任何东西要“出队”,例如......这种“尝试模式”通常用于整个FCL和BCL元素。这与锁定无关,并发集合(或者至少应该是)主要是在没有锁定的情况下实现的......

相关问题