2009-11-06 8 views
11

私は、今後の.NET 4.0フレームワークでConcurrentBag<T>クラスの存在によって、自分自身が非常に興味をそそら見つける:.NETのConcurrentBag <T>のようなクラスはどのように実装できますか?

バッグは、順序は重要ではありませんオブジェクトを格納するために有用であり、セットとは異なり、バッグは重複をサポートしています。

質問:このアイデアはどのように実装されますか?私がよく知っているほとんどのコレクションは、本質的には、配列のいくつかの形で、「重要」ではないかもしれませんが、です(これは、列挙は、同じシーケンス内のListQueueStackなどと変わらないコレクションをほとんど常に通ります。

私が推測しなければならないことは、内部的にはDictionary<T, LinkedList<T>>である可能性があります。実際にはのいずれかタイプTをキーとして使用することは理にかなっていないと考えて、実際はかなり疑わしいようです。

私が期待しているのは、これが実際には既にどこかで「考え出された」確立されたオブジェクトタイプであり、この確立されたタイプを知っている誰かがそれについて教えてくれるということです。実生活では理解しやすいが、開発者として利用可能なクラスに変換するのは難しいという概念のひとつであり、私はその可能性について好奇心が強いのです。

EDIT

一部レスポンダはBagが内部ハッシュテーブルの形かもしれないことを示唆しています。これは私の最初の考えでもありましたが、私はこの考え方に2つの問題があると予期しました:

  1. 問題のタイプに適切なハッシュコード関数がないときにハッシュテーブルが役立つわけではありません。
  2. コレクション内のオブジェクトの「カウント」を単に追跡することは、オブジェクトを格納することと同じではありません。

メタナイトが示唆したように、おそらく例これは、より明確になるだろう:

public class ExpensiveObject() { 
    private ExpensiveObject() { 
     // very intense operations happening in here 
    } 

    public ExpensiveObject CreateExpensiveObject() { 
     return new ExpensiveObject(); 
    } 
} 

static void Main() { 
    var expensiveObjects = new ConcurrentBag<ExpensiveObject>(); 

    for (int i = 0; i < 5; i++) { 
     expensiveObjects.Add(ExpensiveObject.CreateExpensiveObject()); 
    } 

    // after this point in the code, I want to believe I have 5 new 
    // expensive objects in my collection 

    while (expensiveObjects.Count > 0) { 
     ExpensiveObject expObj = null; 
     bool objectTaken = expensiveObjects.TryTake(out expObj); 
     if (objectTaken) { 
      // here I THINK I am queueing a particular operation to be 
      // executed on 5 separate threads for 5 separate objects, 
      // but if ConcurrentBag is a hashtable then I've just received 
      // the object 5 times and so I am working on the same object 
      // from 5 threads at the same time! 
      ThreadPool.QueueUserWorkItem(DoWorkOnExpensiveObject, expObj); 
     } else { 
      break; 
     } 
    } 
} 

static void DoWorkOnExpensiveObject(object obj) { 
    ExpensiveObject expObj = obj as ExpensiveObject; 
    if (expObj != null) { 
     // some work to be done 
    } 
} 
+0

+1このクラスの存在を知っておいてよろしいですか? – Konamiman

+0

Dan-o:あなたのサンプルコードの5行のコメントは意味がありません。もちろん、あなたはその時点でそのバッグに5つの独立したオブジェクトを持っています。[public ExpensiveObject CreateExpensiveObject()]の "new"演算子はそれを保証します。 – Boogaloo

+0

mmm。私の間違い。私は過去にハッシュを使用していません。私は、デフォルトのハッシュ・ジェネレータがオブジェクトごとに一意のハッシュ値を作成すると仮定していました。これは独自のハッシュ・ジェネレータでオーバーライドできます。私を気にしないでください。 :) – Boogaloo

答えて

8

あなたがConcurrentBag<T>の詳細を見れば、あなたはそれは、内部的に、基本的にリンクされカスタマイズされますことがわかりますリスト。

バッグには重複が含まれている可能性があり、インデックスでアクセスできないため、二重リンクされたリストは実装には非常に適しています。これにより、挿入と削除のためにかなり細かいロックが可能になります(コレクション全体をロックする必要はなく、挿入/削除する場所のノードだけをロックする必要はありません)。重複が心配されていないので、ハッシングはありません。これにより、二重リンクリストが完璧になります。

+0

良い点:私はバッグがマッチングする必要がないという事実についても考えていませんでした。ただオブジェクトを取り出して返します。私が真に混乱している問題を思い起こさせるように思えたのは、突然、困惑するようなことではないようです。 –

+0

他の回答者の回答が異なるので(あなたの意見が私に最も合っていますが)、あなたがこれらの詳細を見つけた場所を知りたいのですが?また、それは挿入/削除が起こっているノードをロックするだけの大きなポイントです...私は、ConcurrentQueue が実際には内部で 'LinkedList 'であることを知りたいと思います? (そうでなければ、エンキュー/デキューが不必要に高価になるようです)。 –

+0

.NET 4 Beta 2では、ReflectorをSystem.dllで使用することができます。完全な実装がわかります。実際にはLinkedList ではなく、内部ConcurrentBag .ThreadLocalList ConcurrentBag .Nodeを使用していますが、基本的にはカスタマイズされたダブルリンクリストです。 –

0

順序は関係ありません。ConcurrentBagは、データの高速取得を可能にするために、バックグラウンドでハッシュテーブルを使用している可能性があります。しかし、Hashsetとは異なり、バッグは重複を受け入れます。たぶん、各アイテムは、アイテムが追加されたときに1に設定されるCountプロパティとペアになる可能性があります。同じアイテムをもう一度追加する場合は、このアイテムのCountプロパティを増分するだけです。

次に、1より大きい数のアイテムを削除するには、このアイテムのCountを減らすだけです。カウントが1だった場合は、Item-Countペアをハッシュテーブルから削除します。

+0

あなたと同じように思えますが、私も同様のアイデアを持っていましたが、これを考慮してください。まず、ConcurrentBag の使用をキーとして使用するのに適したタイプに制限します。第二に、単に 'ハッシュセット'の各エントリに 'Count'プロパティを持っていれば、そのオブジェクトは実際には*バッグ内ではなく、基本的に目的を破ると感じます。 (つまり、私が 'ConcurrentBag 'に同じ 'Thing'を10コピー持っていて、' TryTake'を10回呼び出すと、最初に何が返されるのでしょうか?同じ 'Thing'ですか?しかし、本当に私はちょうど1を持っています。) –

+0

2つの項目がConcurrentBagで重複していると見なされる場合、それらは同じではありませんか?それらが同じであれば、TryTakeを何回か呼び出すと、まったく同じオブジェクトが得られます。私はConcurrentBagが "キーとして使用するのに適していない型"をどのようにうまく機能するかはわかりません... –

+0

具体的な例があれば、あなたが言及した問題を多く視覚化するのに役立ちます;-) –

0

さて、Smalltalk(Bagの概念が由来しています)では、コレクションは基本的には複製と同じですが、ハッシュと同じです。しかし、複製オブジェクトを記憶する代わりに、「発生カウント」、例えば各オブジェクトの参照カウントを維持する。 ConcurrentBagが忠実な実装である場合、これは出発点になるはずです。

0

「バッグ」のコンセプトは「マルチセット」と同義です。

実装方法に興味がある場合は、オープンソースの "Bag"/"Multiset"実装(これらはJavaのことです)があります。

これらの実装では、ニーズに応じてさまざまな方法で「バッグ」を実装できます。 TreeMultiset、HashMultiset、LinkedHashMultiset、ConcurrentHashMultisetの例があります。

Googleのコレクション
Googleが"MultiSet" implementations、1 ConcurrentHashMultisetであることがいくつかあります。

Apache Commons
Apacheには多くの「Bag」実装があります。

2

ConcurrentBag上でいくつかの良い情報がここにあります:http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/03/c.net-little-wonders-concurrentbag-and-blockingcollection.aspx

ConcurrentBagは を動作する方法は(.NET 4.0のために System.Threadingに新)新 ThreadLocalのタイプを利用することになるように、 バッグを使用している各スレッドは、そのスレッドにローカルにリスト を持っています。

これは、 にスレッドローカルリストを追加または削除するには、非常に低い の同期が必要であることを意味します。問題は、 にあります。スレッドは、項目 を消費しますが、ローカルリストは空です。この ケースでは、バッグは "作業盗用" を実行し、リスト内のアイテムを持つ別の スレッドからアイテムを奪取します。 これは、より高いレベルの 同期を必要とします。これにより、取り込み操作に少しのオーバーヘッドが追加されます( )。

関連する問題