141

型に関する情報が足りなかったConcurrentDictionaryので、ここで聞いてみようと思いました。

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

私は最近、.NET 4.0に一連のスレッドセーフなコレクションがあることを知りましたが、それは非常に喜ばしいようです。Dictionary同期アクセスを使用する通常のオプションと、ConcurrentDictionaryすでにスレッドセーフなオプションのどちらかを選択できるので、「より効率的で管理しやすい」オプションは何でしょうか。

.NET4.0への参照ConcurrentDictionary

4

7 に答える 7

160

スレッドセーフなコレクションと非スレッドセーフなコレクションは、別の見方ができます。

レジ以外に店員がいない店舗を考えてみましょう。人々が責任を持って行動しないと、多くの問題が発生します。たとえば、店員が現在ピラミッドを構築しているときに、顧客がピラミッド缶から缶を取り出したとしましょう。または、2 人の顧客が同時に同じ商品に到達した場合、どちらが勝ちますか? 戦いはありますか?これはスレッドセーフではないコレクションです。問題を回避する方法はたくさんありますが、いずれもある種のロック、または何らかの方法での明示的なアクセスが必要です。

一方、デスクに店員がいる店を考えてみましょう。店員を通してしか買い物をすることができません。あなたが列に並び、彼にアイテムを頼むと、彼がそれを持ってきて、あなたは列から出ます。複数のアイテムが必要な場合は、各往復で覚えている限りの数のアイテムしかピックアップできませんが、店員を独り占めしないように注意する必要があります。これにより、後ろに並んでいる他の顧客を怒らせることになります.

これを考えてみましょう。店員が一人の店で、列の一番前まで行って、店員に「トイレットペーパーはありますか」と聞いたら、「はい」と答えたら、「わかりました。」いくら必要かわかったらまた連絡します」と言って、列の先頭に戻る頃には、もちろん店は売り切れている可能性があります. このシナリオは、スレッドセーフ コレクションによって防止されません。

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

スレッドセーフでないコレクションには、そのような保証はありません。たとえば、あるスレッドでバイナリ ツリーに何かを追加した場合、別のスレッドがツリーのバランスを取り直すのに忙しくしているときに、アイテムが追加されるという保証はありません。

ただし、スレッドセーフなコレクションは、スレッド上の順次操作がすべてその内部データ構造の同じ「スナップショット」で機能することを保証しません。つまり、次のようなコードがある場合:

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

tree.Counttree.First()の間で、別のスレッドがツリー内の残りのノードをクリアしたため、NullReferenceException が発生する可能性があります。つまり、First()が を返しnullます。

このシナリオでは、問題のコレクションに必要なものを安全に取得できる方法があるかどうかを確認する必要があります。おそらく、上記のコードを書き直す必要があるか、ロックする必要があるかもしれません。

于 2009-12-27T17:23:37.210 に答える
74

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

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

したがって、それらを使用してください。ただし、すべての並行性の問題を無視するための無料パスが得られるとは思わないでください。まだまだ注意が必要です。

于 2009-12-22T21:10:55.397 に答える
45

内部的には、ConcurrentDictionary はハッシュ バケットごとに個別のロックを使用します。単一のエントリで機能する Add/TryGetValue などのメソッドのみを使用する限り、ディクショナリはほとんどロックのないデータ構造として機能し、それぞれの優れたパフォーマンス上の利点があります。OTOH 列挙メソッド (Count プロパティを含む) はすべてのバケットを一度にロックするため、同期されたディクショナリよりもパフォーマンスが低下します。

ConcurrentDictionary を使用するだけです。

于 2010-12-26T10:17:19.290 に答える
18

ConcurrentDictionary.GetOrAdd メソッドは、まさにほとんどのマルチスレッド シナリオに必要なものだと思います。

于 2009-12-27T16:36:53.387 に答える
14

.Net3.5sp1のリアクティブ拡張機能を見たことがありますか。Jon Skeetによると、彼らは.Net3.5sp1の並列拡張機能と同時データ構造のバンドルをバックポートしました。

.Net 4 Beta 2のサンプルのセットがあり、並列拡張の使用方法について非常に詳細に説明されています。

先週、32スレッドを使用してI/Oを実行するConcurrentDictionaryをテストしました。宣伝どおりに機能しているようです。これは、膨大な量のテストが行​​われたことを示しています。

編集:.NET4ConcurrentDictionaryとパターン。

Microsoftは、Patterns ofParalellProgrammingというPDFをリリースしました。.Net 4 Concurrent拡張機能に使用する適切なパターンと、避けるべきアンチパターンについて詳しく説明しているので、ダウンロードする価値があります。ここにあります。

于 2010-01-05T16:51:54.007 に答える
5

基本的に、新しい ConcurrentDictionary を使用します。箱から出してすぐに、スレッド セーフなプログラムを作成するためのコードを少なくする必要があります。

于 2009-12-22T21:10:43.977 に答える
-1

Is this example thread safe?のソリューションと同様に、キャッシュされたコレクションに ConcurrentDictionary を使用しました。これは 1 時間ごとに再設定され、複数のクライアント スレッドによって読み取られます。質問。

ReadOnlyDictionaryに変更すると、  全体的なパフォーマンスが向上することがわかりました。

于 2013-05-05T04:06:11.097 に答える