2012-01-26 129 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