web-dev-qa-db-ja.com

指定されたアイテムの削除をサポートする同時収集?

非常に単純:ConcurrentDictionary(必要な場合に使用しますが、実際には正しい概念ではありません)以外に、アイテムまたは述語の単純な同等性に基づいて特定のアイテムの削除をサポートするConcurrentコレクション(IProducerConsumer実装)はありますか?削除の条件を定義しますか?

説明:マルチスレッド、マルチステージのワークフローアルゴリズムがあります。これは、DBからオブジェクトをプルし、それらを「開始」キューに固定します。そこから、それらは次の段階で取得され、さらに作業され、他のキューに詰め込まれます。このプロセスは、さらにいくつかの段階を経て継続されます。一方、最初のステージはスーパーバイザーによって再度呼び出され、DBからオブジェクトを引き出します。オブジェクトには、まだ処理中のオブジェクトを含めることができます(処理が完了していないため、フラグが設定された状態で再永続化されていないため)。それらは完了しました)。

私が設計しているソリューションは、マスターの「作業中」のコレクションです。オブジェクトは、最初のステージで処理するために取得されるとそのキューに入れられ、ワークフローのどのステージで必要な処理が完了したかによって「処理済み」としてDBに再保存された後に削除されます。オブジェクトがそのリストにある間、最初のステージで再取得された場合は無視されます。

ConcurrentBagを使用する予定でしたが、唯一の削除方法(TryTake)は、指定されたアイテムではなく、任意のアイテムをバッグから削除します(ConcurrentBagは.NET 4ではslowです)。 ConcurrentQueueとConcurrentStackは、次に提供されるアイテム以外のアイテムの削除も許可せず、ConcurrentDictionaryを残します。これは機能しますが、必要以上のものです(本当に必要なのは、処理中のレコードのIDを保存することだけです。ワークフロー中には変更されません)。

21
KeithS

このようなデータ構造がない理由は、すべてのコレクションのルックアップ操作時間がO(n)であるためです。これらはIndexOfRemove(element)などです。これらはすべてすべての要素を列挙し、それらが等しいかどうかをチェックします。

ルックアップ時間がO(1)のハッシュテーブルのみ。並行シナリオでは、O(n)ルックアップ時間により、コレクションのロックが非常に長くなります。この間、他のスレッドは要素を追加できません。

辞書では、ハッシュがヒットしたセルのみがロックされます。他のスレッドは、ハッシュセル内の要素を介して同等性をチェックしている間、追加を続けることができます。

私のアドバイスは続けて、ConcurrentDictionaryを使用することです。


ちなみに、ConcurrentDictionaryはソリューションに対して少し大きすぎるというのは正しいことです。本当に必要なのは、オブジェクトが機能しているかどうかをすばやく確認することです。 HashSetはそのために最適です。基本的には、Add(element)Contains(element)Remove(element)は何もしません。 JavaにはConcurrentHeshSet実装があります。 c#の場合、私はこれを見つけました: 。NetでConcurrentHashSetを実装する方法 それがどれほど優れているかわかりません。

最初のステップとして、HashSetの周りにConcurrentDictionaryインターフェイスを備えたラッパーを作成して実行し、さまざまな実装を試してパフォーマンスの違いを確認します。

18

他の投稿ですでに説明されているように、デフォルトではQueueまたはConcurrentQueueからアイテムを削除することはできませんが、実際に回避する最も簡単な方法は、アイテムを拡張またはラップすることです。

public class QueueItem
{
    public Boolean IsRemoved { get; private set; }
    public void Remove() { IsRemoved = true; }
}

そして、デキューするとき:

QueueItem item = _Queue.Dequeue(); // Or TryDequeue if you use a concurrent dictionary
if (!item.IsRemoved)
{
    // Do work here
}
5
Felix K.

一般的な意味でコレクションをスレッドセーフにするのは本当に難しいです。スレッドセーフには、ライブラリ/フレームワーククラスの責任または範囲外であり、真に「スレッドセーフ」である能力に影響を与える非常に多くの要因があります...あなたが指摘した欠点の1つアウトはパフォーマンスです。最悪の事態を想定しなければならないため、スレッドセーフでもあるパフォーマンスの高いコレクションを作成することは不可能です...

一般的に推奨される方法は、必要なコレクションを使用し、スレッドセーフな方法でそれにアクセスすることです。これが基本的に、フレームワークにスレッドセーフなコレクションがこれ以上ない理由です。詳細については、 http://blogs.msdn.com/b/bclteam/archive/2005/03/15/396399.aspx#9534371 を参照してください。

1
Peter Ritchie