50

对问题“为什么这个 Parallel.ForEach 代码冻结程序?”的公认答案 建议在 WPF 应用程序 中用ConcurrentBag替换 List 用法。

我想了解在这种情况下是否可以使用 BlockingCollection ?

4

4 回答 4

82

您确实可以使用 a BlockingCollection,但这样做绝对没有意义。

首先,请注意,它BlockingCollection是实现IProducerConsumerCollection<T>. 任何实现该接口的类型都可以用作底层存储:

创建BlockingCollection<T>对象时,您不仅可以指定有界容量,还可以指定要使用的集合类型。例如,您可以ConcurrentQueue<T>为先进先出 (FIFO) 行为指定对象,或ConcurrentStack<T>为后进先出 (LIFO) 行为指定对象。您可以使用任何实现该IProducerConsumerCollection<T>接口的集合类。的默认集合类型BlockingCollection<T>ConcurrentQueue<T>.

这包括ConcurrentBag<T>,这意味着您可以拥有一个阻塞并发包。IProducerConsumerCollection<T>那么普通集合和阻塞集合有什么区别呢?文件BlockingCollection说(强调我的):

BlockingCollection<T>用作 IProducerConsumerCollection<T>实例的包装器,允许从集合中删除尝试被阻止,直到数据可以被删除。类似地,BlockingCollection<T>可以创建 a 来强制[...]中允许的 数据元素数量的上限。IProducerConsumerCollection<T>

由于在链接的问题中不需要做任何这些事情, usingBlockingCollection只需添加一层未使用的功能。

于 2013-03-14T09:20:16.433 回答
29
  • List<T>是一个设计用于单线程应用程序的集合。

  • ConcurrentBag<T>是一类Collections.Concurrent命名空间,旨在简化在多线程环境中使用集合。如果您使用 ConcurrentCollection,您将不必锁定您的集合以防止其他线程损坏。您可以从您的集合中插入或获取数据,而无需编写特殊的锁定代码。

  • BlockingCollection<T>旨在摆脱检查线程之间共享集合中是否有新数据可用的要求。如果有新数据插入到共享集合中,那么您的消费者线程将立即唤醒。因此,您不必检查新数据是否在特定时间间隔内可用于消费者线程,通常是在 while 循环中。

于 2016-10-31T14:25:24.810 回答
7

每当您发现需要线程安全List<T>时,在大多数情况下, theConcurrentBag<T>和 theBlockingCollection<T>都不是您的最佳选择。这两个集合都专门用于促进生产者-消费者场景,因此除非您有多个线程同时从集合中添加删除项目,否则您应该寻找其他选项(ConcurrentQueue<T>在大多数情况下最好的候选者是)。

特别是关于,它是一个针对混合生产者 - 消费者场景ConcurrentBag<T>的非常专业的类。这意味着每个工作线程都应该是生产者和消费者(从同一个集合中添加和删除项目)。可能是类内部存储的一个很好的候选,但除此之外,很难想象这个类有任何有利的使用场景。ObjectPool

人们通常认为 theConcurrentBag<T>是线程安全的等价物 a List<T>,但事实并非如此。这两个 API 的相似性具有误导性。调用结果AddList<T>在列表末尾添加项目。调用结果而不是在包内的随机插槽中添加的项目Add。本质上是无序ConcurrentBag<T>的。ConcurrentBag<T>没有针对枚举进行优化,并且在被命令这样做时做得很糟糕。它在内部维护了一堆线程本地队列,因此其内容的顺序取决于哪个线程做了什么,而不是什么时候发生的。

这些特性使得存储/循环ConcurrentBag<T>结果的选择不太理想。Parallel.ForParallel.ForEach

一个更好的线程安全替代方法List<T>.AddConcurrentQueue<T>.Enqueue方法。"Enqueue"是一个不如"Add"熟悉的词,但它实际上做了你期望它做的事情。

没有什么是aConcurrentBag<T>可以做的,a是不能做的ConcurrentQueue<T>。例如,这两个集合都没有提供从集合中删除特定项目的方法。TryRemove如果您想要一个带有参数的方法的并发集合key,您可以查看ConcurrentDictionary<K,V>该类。

于 2020-11-13T14:57:47.650 回答
3

是的,你可以使用BlockingCollection它。finishedProxies将被定义为:

BlockingCollection<string> finishedProxies = new BlockingCollection<string>();

并添加一个项目,你会写:

finishedProxies.Add(checkResult);

完成后,您可以根据内容创建一个列表。

于 2013-03-14T03:57:08.413 回答