我正在尝试构建订阅列表。我们举个例子:
发布商列表,每个发布商都有一个杂志列表,每个杂志都有一个订阅者列表
发布商 - >杂志 - >订户
在C#中的词典中使用词典中的词典是有意义的。在没有竞争条件的情况下添加/删除用户时,是否可以在不锁定整个结构的情况下执行此操作?
此外,代码在C#中变得非常混乱,这让我觉得我没有走正确的道路。有更简单的方法吗?以下是构造函数和订阅方法:
注意:代码使用Source,Type,Subscriber而不是上面的名称
来源--->类型--->订户
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 :(得分:2)
如果你使用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());
}