对问题“为什么这个 Parallel.ForEach 代码冻结程序?”的公认答案 建议在 WPF 应用程序 中用ConcurrentBag替换 List 用法。
我想了解在这种情况下是否可以使用 BlockingCollection ?
对问题“为什么这个 Parallel.ForEach 代码冻结程序?”的公认答案 建议在 WPF 应用程序 中用ConcurrentBag替换 List 用法。
我想了解在这种情况下是否可以使用 BlockingCollection ?
您确实可以使用 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
只需添加一层未使用的功能。
List<T>
是一个设计用于单线程应用程序的集合。
ConcurrentBag<T>
是一类Collections.Concurrent
命名空间,旨在简化在多线程环境中使用集合。如果您使用 ConcurrentCollection,您将不必锁定您的集合以防止其他线程损坏。您可以从您的集合中插入或获取数据,而无需编写特殊的锁定代码。
BlockingCollection<T>
旨在摆脱检查线程之间共享集合中是否有新数据可用的要求。如果有新数据插入到共享集合中,那么您的消费者线程将立即唤醒。因此,您不必检查新数据是否在特定时间间隔内可用于消费者线程,通常是在 while 循环中。
每当您发现需要线程安全List<T>
时,在大多数情况下, theConcurrentBag<T>
和 theBlockingCollection<T>
都不是您的最佳选择。这两个集合都专门用于促进生产者-消费者场景,因此除非您有多个线程同时从集合中添加和删除项目,否则您应该寻找其他选项(ConcurrentQueue<T>
在大多数情况下最好的候选者是)。
特别是关于,它是一个针对混合生产者 - 消费者场景ConcurrentBag<T>
的非常专业的类。这意味着每个工作线程都应该是生产者和消费者(从同一个集合中添加和删除项目)。它可能是类内部存储的一个很好的候选,但除此之外,很难想象这个类有任何有利的使用场景。ObjectPool
人们通常认为 theConcurrentBag<T>
是线程安全的等价物 a List<T>
,但事实并非如此。这两个 API 的相似性具有误导性。调用结果Add
以List<T>
在列表末尾添加项目。调用结果而不是在包内的随机插槽中添加的项目Add
。本质上是无序ConcurrentBag<T>
的。ConcurrentBag<T>
它没有针对枚举进行优化,并且在被命令这样做时做得很糟糕。它在内部维护了一堆线程本地队列,因此其内容的顺序取决于哪个线程做了什么,而不是什么时候发生的。
这些特性使得存储/循环ConcurrentBag<T>
结果的选择不太理想。Parallel.For
Parallel.ForEach
一个更好的线程安全替代方法List<T>.Add
是ConcurrentQueue<T>.Enqueue
方法。"Enqueue"是一个不如"Add"熟悉的词,但它实际上做了你期望它做的事情。
没有什么是aConcurrentBag<T>
可以做的,a是不能做的ConcurrentQueue<T>
。例如,这两个集合都没有提供从集合中删除特定项目的方法。TryRemove
如果您想要一个带有参数的方法的并发集合key
,您可以查看ConcurrentDictionary<K,V>
该类。
是的,你可以使用BlockingCollection
它。finishedProxies
将被定义为:
BlockingCollection<string> finishedProxies = new BlockingCollection<string>();
并添加一个项目,你会写:
finishedProxies.Add(checkResult);
完成后,您可以根据内容创建一个列表。