C# 线程安全订阅列表的最佳数据结构?

C# 线程安全订阅列表的最佳数据结构?,c#,data-structures,collections,thread-safety,C#,Data Structures,Collections,Thread Safety,我正在尝试建立订阅列表。让我们举个例子: 出版商名单,每一家都有一份杂志名单,每一家都有一份订户名单 发布者-->杂志-->订阅者 在C#中的词典中使用词典是有意义的。在添加/删除没有竞争条件的订阅服务器时,是否可以在不锁定整个结构的情况下执行此操作 而且C语言中的代码很快就会变得混乱,这让我觉得我走的路不对。有没有更简单的方法?以下是构造函数和订阅方法: 注意:代码使用源代码、类型、订阅服务器而不是上面的名称 来源-->类型-->订户 公共类SubscriptionCollection { /

我正在尝试建立订阅列表。让我们举个例子:

出版商名单,每一家都有一份杂志名单,每一家都有一份订户名单

发布者-->杂志-->订阅者

在C#中的词典中使用词典是有意义的。在添加/删除没有竞争条件的订阅服务器时,是否可以在不锁定整个结构的情况下执行此操作

而且C语言中的代码很快就会变得混乱,这让我觉得我走的路不对。有没有更简单的方法?以下是构造函数和订阅方法:

注意:代码使用源代码、类型、订阅服务器而不是上面的名称

来源-->类型-->订户

公共类SubscriptionCollection
{
//我确定这里的比赛条件!还没有锁定任何东西,但应该在某个时候重新考虑
ConcurrentDictionary源类型subs;
公共订阅集合()
{
SourceTypeSubs=新的ConcurrentDictionary();
}
public void Subscribe(SourceT SourceT、TypeT TypeT、SubscriberT subT){
ConcurrentDictionary类型和Subs;
if(SourceTypeSubs.TryGetValue(sourceT、out-types和subs))
{
并发字典子类;
if(类型和子类型TryGetValue(类型T,输出子类型))
{
订阅信息子信息;
if(subs.TryGetValue(subT,out subInfo))
{
//订阅已存在-不执行任何操作
}
其他的
{
TryAdd(subT,newsubscriptioninfo());
}
}
其他的
{
//此类型不存在-请先添加类型,然后添加订阅
var newType=新的ConcurrentDictionary();
TryAdd(subT,newsubscriptioninfo());
typesANDsubs.TryAdd(typeT,newType);
}
}
其他的
{
//此源不存在-首先添加源,然后键入,然后订阅
var newSource=新的ConcurrentDictionary();
var newType=新的ConcurrentDictionary();
TryAdd(subT,newsubscriptioninfo());
TryAdd(typeT,newType);
TryAdd(sourceT,newSource);
};
}

如果您使用
ConcurrentDictionary
,就像您已经使用的那样,您不需要锁定,这已经得到了解决

但是您仍然需要考虑竞争条件以及如何处理它们。幸运的是,
ConcurrentDictionary
正好提供您所需的内容。例如,如果您有两个线程,它们都试图同时订阅尚不存在的源代码,则其中只有一个会成功。但这就是为什么
TryAdd()
返回添加是否成功。您不能忽略它的返回值。如果它返回
false
,您知道其他线程已经添加了该源,因此您现在可以检索字典

另一个选项是使用。它检索已经存在的值,如果它还不存在,则创建它

我会像这样重写您的代码(并使其更简单):

public void Subscribe(SourceT SourceT,TypeT TypeT,SubscriberT subT)
{
var typesAndSubs=SourceTypeSubs.GetOrAdd(sourceT,
_=>新建ConcurrentDictionary());
变量subs=typesAndSubs.GetOrAdd(typeT,
_=>新建ConcurrentDictionary());
GetOrAdd(subT,=>newsubscriptioninfo());
}

这个问题是C#特定的,还是你在寻找一种可以在任何地方使用的方法?真的是在任何地方。然后我可以将它应用到C#我问的是因为如果这个问题是C#特定的,那么在.Net框架中就可以直接使用类。好吧,我想建议
ConcurrentDictionary
,但刚才注意到了至少,您已经在使用它了。GetOrAdd确实使代码更干净了,因为我不必检查TryAdd的返回值并重试(可能几次?)确保每个线程最终都能成功通过。如果没有其他问题通过,Upvoting for now and then将选择作为接受答案。非常感谢!!@HarryMexican,您不必多次执行
TryAdd()
。只有当其他人已经添加了具有相同密钥的项时,才会失败。
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());
}