2012-01-26 6 views
0

サブスクリプションリストを作成しようとしています。サブスクリプションのスレッドセーフなリストのための最善のデータ構造ですか?

は、辞書の使用ことは理にかなっています>加入者 - >雑誌 - 、各雑誌のリストを持つ、各加入者のリストを持っ

出版出版社の

リスト:のは、例を見てみましょうC#のディクショナリ内のディクショナリ内。競争条件なしで加入者を追加/削除するときに構造全体をロックすることなくこれを行うことは可能ですか?

また、C#でコードが非常に乱雑になり、正しいパスを辿ることができないと思います。これを行う簡単な方法はありますか?ここでは、コンストラクタであり、この方法をサブスクライブ:

注:コードは

ソースの上にソース、タイプ、加入者の代わりに名前を使用しています--->タイプ--->加入者

public class SubscriptionCollection<SourceT, TypeT, SubscriberT> 
{ 
// Race conditions here I'm sure! Not locking anything yet but should revisit at some point 

ConcurrentDictionary<SourceT, ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>> SourceTypeSubs; 

public SubscriptionCollection() 
{ 
    SourceTypeSubs = new ConcurrentDictionary<SourceT, ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>>(); 
} 

public void Subscribe(SourceT sourceT, TypeT typeT, SubscriberT subT) { 

    ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>> typesANDsubs; 
    if (SourceTypeSubs.TryGetValue(sourceT, out typesANDsubs)) 
    { 
     ConcurrentDictionary<SubscriberT, SubscriptionInfo> subs; 
     if (typesANDsubs.TryGetValue(typeT, out subs)) 
     { 

      SubscriptionInfo subInfo; 
      if (subs.TryGetValue(subT, out subInfo)) 
      { 
       // Subscription already exists - do nothing 

      } 
      else 
      { 
       subs.TryAdd(subT, new SubscriptionInfo()); 
      } 
     } 
     else 
     { 
      // This type does not exist - first add type, then subscription 
      var newType = new ConcurrentDictionary<SubscriberT, SubscriptionInfo>(); 
      newType.TryAdd(subT, new SubscriptionInfo()); 
      typesANDsubs.TryAdd(typeT, newType); 

     } 

    } 
    else 
    { 
     // this source does not exist - first add source, then type, then subscriptions 
     var newSource = new ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>(); 
     var newType = new ConcurrentDictionary<SubscriberT, SubscriptionInfo>(); 
     newType.TryAdd(subT, new SubscriptionInfo()); 
     newSource.TryAdd(typeT, newType); 
     SourceTypeSubs.TryAdd(sourceT, newSource); 
    }; 
} 
+0

は、質問C#の固有のですか、どこでも使用することができますアプローチを探していますか? – svick

+0

どこでも本当に..私はそれをC# –

+0

に適合させることができます質問がC#固有であれば、使用できる.Netフレームワークに直接クラスがあるので、私は尋ねています。 – svick

答えて

1

場合すでに使用しているようにConcurrentDictionaryを使用すると、ロックが必要なくなり、すでに処理されています。

しかし、レースの状態と対処方法について考える必要があります。幸いなことに、ConcurrentDictionaryは必要なものを正確に提供します。たとえば、2つのスレッドがあり、同時に存在しないソースにサブスクライブしようとすると、そのうちの1つだけが成功します。しかし、それで、追加が成功したかどうかをTryAdd()が返してくれるのです。その戻り値を無視することはできません。 falseが返された場合は、他のスレッドがすでにそのソースを追加していることを知っているので、今すぐ辞書を取得できます。

もう1つの方法はthe GetOrAdd() methodです。それは既に存在する値を検索し、それがまだ存在しない場合に作成します。

私はこのようなあなたのコードを書き換える(と道に沿って、それがはるかに簡単に)になります。

public void Subscribe(SourceT sourceT, TypeT typeT, SubscriberT subT) 
{ 
    var typesAndSubs = SourceTypeSubs.GetOrAdd(sourceT, 
     _ => new ConcurrentDictionary<TypeT, ConcurrentDictionary<SubscriberT, SubscriptionInfo>>()); 

    var subs = typesAndSubs.GetOrAdd(typeT, 
     _ => new ConcurrentDictionary<SubscriberT, SubscriptionInfo>()); 

    subs.GetOrAdd(subT, _ => new SubscriptionInfo()); 
} 
+0

GetOrAddは、TryAddの戻り値をチェックして、各スレッドが最終的に成功するのを確実にするために何度も試してみる必要はないので、コードをもっとクリーンにします。今のところアップアップして、それ以外の何も来なければ受け入れられた答えとして選択します。どうもありがとう!! –

+0

@HarryMexican、あなたは 'TryAdd()'を何度も行う必要はありません。他の誰かがすでに同じキーを持つアイテムを追加した場合にのみ失敗する可能性があります。 – svick

関連する問題