2012-01-26 50 views
0

我正在嘗試構建訂閱列表。讓我們的例子:出版商訂閱的線程安全列表的最佳數據結構?

列表,每個都具有雜誌的列表,每個都具有用戶列表

出版商 - >雜誌 - >訂閱

有道理使用字典在C#中的Dictionary中的字典中。添加/刪除沒有競爭條件的用戶時,是否可以在不鎖定整個結構的情況下執行此操作?

此外,代碼在C#中非常迅速地變得混亂,這使我認爲我不會走正確的道路。有沒有更簡單的方法來做到這一點?下面是構造函數和subscribe方法:

注:該代碼使用來源,類型,用戶不使用名字而上述

源--->類型--->用戶

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正是您所需要的。例如,如果您有兩個線程,它們都嘗試同時訂閱尚不存在的源代碼,則只有其中一個會成功。但這就是爲什麼TryAdd()返回添加是否成功。你不能忽略它的返回值。如果它返回false,則知道其他某個線程已經添加了該源,因此您現在可以檢索該字典。

另一種選擇是使用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的返回值並再次嘗試(可能多次?)以確保每個線程都能成功通過。如果沒有其他事情發生,現在和時間的upvoting將選擇爲接受的答案。非常感謝!! –

+0

@HarryMexican,你不必做'TryAdd()'幾次。只有其他人添加了具有相同密鑰的項目時,它纔會失敗。 – svick