我最近注意到,在System.Collections.Concurrent命名空间中包含的集合对象中,通常会看到Collection.TrySomeAction()
而不是Collection.SomeAction()
。
这是什么原因?我认为它与锁定有关?
所以我想知道在什么条件下尝试(例如)从堆栈,队列,包等中取出项目失败?
答案 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元素。这与锁定无关,并发集合(或者至少应该是)主要是在没有锁定的情况下实现的......