「このParallel.ForEachコードはなぜプログラムをフリーズするのか?」という質問に対する受け入れられた答え は、WPFアプリケーションで ConcurrentBag によってリストの使用を置き換えることを推奨しています。
この場合、代わりに BlockingCollection を使用できるかどうかを理解したいのですが?
確かにBlockingCollection
を使用できますが、そうすることにはまったく意味がありません。
まず、 BlockingCollection
は IProducerConsumerCollection<T>
を実装するコレクションのラッパーであることに注意してください。そのインターフェイスを実装する任意のタイプは、基礎となるストレージとして使用できます。
BlockingCollection<T>
オブジェクトを作成するとき、制限された容量だけでなく、使用するコレクションのタイプも指定できます。たとえば、先入れ先出し(FIFO)動作にはConcurrentQueue<T>
オブジェクトを、後入れ先出し(LIFO)動作にはConcurrentStack<T>
オブジェクトを指定できます。IProducerConsumerCollection<T>
インターフェイスを実装するコレクションクラスを使用できます。BlockingCollection<T>
のデフォルトのコレクションタイプはConcurrentQueue<T>
です。
これにはConcurrentBag<T>
が含まれます。これは、ブロッキングコンカレントバッグを持つことができることを意味します。それでは、プレーンIProducerConsumerCollection<T>
とブロッキングコレクションの違いは何ですか? BlockingCollection
のドキュメントには、次のように記載されています(強調鉱山):
BlockingCollection<T>
はIProducerConsumerCollection<T>
インスタンスのラッパーとして使用されます。データが削除できるようになるまでコレクションからの削除試行をブロックできるようにする。同様に、BlockingCollection<T>
を作成して許可されるデータ要素の数の上限を強制するでIProducerConsumerCollection<T>
[...]
リンクされた質問では、これらのいずれかを行う必要がないため、BlockingCollection
を使用すると、使用されない機能のレイヤーが追加されます。
List<T>
は、シングルスレッドアプリケーションで使用するように設計されたコレクションです。
ConcurrentBag<T>
は、マルチスレッド環境でコレクションの使用を簡素化するために設計されたConcurrentCollection<T>
のサブタイプです。 ConcurrentCollectionを使用する場合、他のスレッドによる破損を防ぐためにコレクションをロックする必要はありません。コレクションにデータを挿入したり、コレクションからデータを取得したりできます。特別なロックコードを記述する必要はありません。
BlockingCollection<T>
は、スレッド間の共有コレクションで新しいデータが利用可能かどうかをチェックする要件を取り除くように設計されています。共有コレクションに新しいデータが挿入されると、コンシューマスレッドはすぐに起動します。そのため、通常はwhileループで、特定の時間間隔でコンシューマスレッドに新しいデータが利用可能かどうかを確認する必要はありません。はい、そのためにBlockingCollection
を使用できます。 finishedProxies
は次のように定義されます。
BlockingCollection<string> finishedProxies = new BlockingCollection<string>();
アイテムを追加するには、次のように記述します。
finishedProxies.Add(checkResult);
そして、完了したら、コンテンツからリストを作成できます。