質問

十分な情報が見つかりませんでした ConcurrentDictionary タイプがあるので、ここで質問させていただきたいと思いました。

現在、私が使用しているのは、 Dictionary 複数のスレッド (スレッド プールからのものであるため、正確なスレッド数はありません) によって常にアクセスされるすべてのユーザーを保持し、同期されたアクセスを持ちます。

最近、.NET 4.0 にスレッドセーフなコレクションのセットがあることを知りました。これは非常に喜ばしいことだと思います。通常のオプションを選択するか、「より効率的で管理が簡単な」オプションは何だろうと考えていました。 Dictionary 同期アクセスを使用するか、 ConcurrentDictionary これはすでにスレッドセーフです。

.NET 4.0 への参照 ConcurrentDictionary

役に立ちましたか?

解決

非スレッドセーフ・コレクション対スレッドセーフなコレクションは、さまざまな方法で時に調べることができます。

チェックアウト時を除き、無店員と店を考えてみましょう。人々が責任を持って行動していない場合は、問題のトンを持っています。たとえば、すべての地獄が緩んで破る、さんは店員が現在のピラミッドを構築している間に、顧客がピラミッド缶から缶を取りましょう。 2人の顧客が同時に同じアイテムに達した場合や、何が、誰が勝ちますか?戦いがあるでしょうか?これは、非スレッドセーフ・コレクションです。そこの問題を回避する方法はたくさんありますが、それらのすべては、いくつかのロックの種類、または何らかの方法または別ではなく、明示的なアクセスを必要とします。

一方、机に店員と店を考慮し、あなただけの彼を通して買い物をすることができます。あなたがオンラインで取得し、アイテムのために彼に尋ねると、彼はあなたに戻ってそれをもたらし、あなたがラインの外に出ます。あなたが複数のアイテムが必要な場合は、あなたが覚えることができるようあなただけの各往復にできるだけ多くのアイテムを拾うことができますが、店員を占有しないように注意する必要があり、これはあなたの後ろの行に他の顧客を怒らます。

さて、これを考慮してください。あなたはすべての方法の行の先頭に取得し、店員に言わせれば何1人の店員、と店では、「あなたは、任意のトイレットペーパーを持っていますか」、と彼は「はい」と言い、その後、あなたは[OK]を、私は」」行きます私は私が必要とするどのくらい知っているとき、」あなたに戻って取得するでしょう、そしてあなたは、バックラインの前にいる時間によって、店はもちろん完売することができます。このシナリオでは、スレッドセーフのコレクションによって防止されていません。

コレクションスレッドセーフAは、その内部データ構造は、複数のスレッドからアクセスした場合でも、常に有効であることを保証します。

非スレッドセーフのコレクションは、どのような保証が付属していません。たとえば、別のスレッドが木をリバランス忙しくているときに、1つのスレッドでのバイナリツリーに何かを追加した場合、アイテムが追加される保証はありません、またはツリーはその後まだ有効であってもということ、それは希望を越えて破損している可能性があります。

コレクションスレッドセーフAは、しかし、あなたはこのようなコードを持っている場合ことを意味し、その内部データ構造の同じ「スナップショット」のスレッド上の一連の動作は、すべての作業、ということを保証するものではありません。

if (tree.Count > 0)
    Debug.WriteLine(tree.First().ToString());

あなたが挟んtree.Counttree.First()ためとNullReferenceExceptionを取得する可能性があります、別のスレッドがFirst()nullを返します意味し、ツリー内の残りのノードをクリアしています。

このシナリオでは、あなたのいずれかは、おそらくあなたは上記のコードを書き換える必要があり、問題のコレクションは、あなたが望む結果を得るために安全な方法を持っているかどうかを確認する必要がある、またはあなたがロックする必要があるかもしれません。

他のヒント

スレッドセーフだからといって、すべてのスレッドの問題を無視できるわけではないため、スレッドセーフなコレクションを使用するときは依然として細心の注意が必要です。コレクションがそれ自体をスレッドセーフであると宣伝する場合、それは通常、複数のスレッドが同時に読み取りおよび書き込みを行っている場合でも、コレクションが一貫した状態を維持していることを意味します。ただし、これは、単一のスレッドが複数のメソッドを呼び出した場合に、結果の「論理的」シーケンスが表示されることを意味するものではありません。

たとえば、最初にキーが存在するかどうかを確認し、後でそのキーに対応する値を取得した場合、ConcurrentDictionary バージョンであってもそのキーは存在しない可能性があります (別のスレッドがキーを削除した可能性があるため)。この場合でも、ロックを使用する必要があります (または、より良い方法:を使用して 2 つの呼び出しを結合します TryGetValue).

したがって、これらを使用することはできますが、同時実行の問題をすべて無視できるフリーパスが与えられるとは考えないでください。まだ注意が必要です。

内部ConcurrentDictionaryは、各ハッシュ・バケットのための個別のロックを使用しています。限り、あなたは/ TryGetValueと単一のエントリに取り組むようなメソッドを追加のみ使用して、辞書にはそれぞれの甘いパフォーマンス上の利点とほぼロックフリーのデータ構造として動作します。 (Countプロパティを含む)列挙メソッド大藤、一度にすべてのバケットをロックしている同期辞書よりもさらに悪いので、性能面ます。

私はちょうどConcurrentDictionaryを使用し、言うと思います。

私はConcurrentDictionary.GetOrAdd方法は、ほとんどのマルチスレッドのシナリオが必要まさにだと思います。

あなたは反応性拡張のための.Net 3.5sp1を見たことがあります。ジョンスキートによると、彼らは.Net3.5 SP1の並列拡張機能との同時データ構造のバンドルをバックポートしている。

並列拡張それらを使用する方法についてのかなり良い詳細に説明する。ネット4のベータ2、のためのサンプルのセットがあります。

私はちょうどI / Oを実行するために32個のスレッドを使用してConcurrentDictionaryをテスト先週費やしてきました。テストの途方もない量がそれに置かれたことを示すと思われる、宣伝通りに動作するようです。

編集:.NET 4 ConcurrentDictionary及びパターン

MicrosoftはParalellプログラミングのパターンと呼ばれるPDFをリリースしています。それは避けること。ネット4の同時拡張機能やアンチパターンを使用することが本当にいい細部に右のパターンを説明するようにダウンロードし、そのreallly価値があります。 ここにある。

基本的には、新しいConcurrentDictionaryに行きたいです。箱から出してすぐ、あなたは、スレッドセーフなプログラムを作るために以下のコードを記述する必要があります。

私たちは、キャッシュされたコレクションのためにConcurrentDictionaryを使用しました。

、それが再投入さごとに1時間、その後、複数のクライアントスレッドで読み取り、同様のにありますのためのソリューションには、この例のスレッドセーフ?の質問です。

私たちは ReadOnlyDictionary の改善、全体的なパフォーマンスにそれを変更することが見出さます。

ConcurrentDictionary それが満たされれば素晴らしい選択肢です 全て スレッドセーフのニーズ。そうでない場合、つまり、少し複雑なことをしている場合は、通常の Dictionary+lock より良い選択肢になるかもしれません。たとえば、いくつかの注文を辞書に追加し、注文の合計金額を常に更新したいとします。次のようなコードを書くことができます。

private ConcurrentDictionary<int, Order> _dict;
private Decimal _totalAmount = 0;

while (await enumerator.MoveNextAsync())
{
    Order order = enumerator.Current;
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

このコードはスレッドセーフではありません。複数のスレッドが更新している _totalAmount フィールドが破損した状態のままになる可能性があります。したがって、次のようにして保護しようとするかもしれません lock:

_dict.TryAdd(order.Id, order);
lock (_locker) _totalAmount += order.Amount;

このコードは「安全」ですが、それでもスレッドセーフではありません。という保証はありません。 _totalAmount 辞書の項目と一致しています。別のスレッドがこれらの値を読み取り、UI 要素を更新しようとする可能性があります。

Decimal totalAmount;
lock (_locker) totalAmount = _totalAmount;
UpdateUI(_dict.Count, totalAmount);

totalAmount 辞書内の注文数と一致しない場合があります。表示された統計が間違っている可能性があります。この時点で、延長する必要があることがわかります。 lock 辞書の更新を含む保護:

// Thread A
lock (_locker)
{
    _dict.TryAdd(order.Id, order);
    _totalAmount += order.Amount;
}

// Thread B
int ordersCount;
Decimal totalAmount;
lock (_locker)
{
    ordersCount = _dict.Count;
    totalAmount = _totalAmount;
}
UpdateUI(ordersCount, totalAmount);

このコードは完全に安全ですが、 ConcurrentDictionary なくなってしまった。

  1. 通常の使用よりもパフォーマンスが悪くなります Dictionary, 、内部の内部ロックが原因で、 ConcurrentDictionary 今では無駄で冗長です。
  2. 共有変数の保護されていない使用がないか、すべてのコードを確認する必要があります。
  3. 厄介な API の使用に行き詰まっています (TryAdd?, AddOrUpdate?).

そこで私のアドバイスは次のとおりです。から始める Dictionary+lock, 、後でアップグレードするオプションを残しておきます。 ConcurrentDictionary このオプションが実際に実行可能な場合は、パフォーマンスの最適化として。多くの場合、そうではありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top