C# 并发ToLookup()转换?
如何使C# 并发ToLookup()转换?,c#,.net,parallel-processing,concurrentdictionary,C#,.net,Parallel Processing,Concurrentdictionary,如何使ToLookup()并发?我有一个类似这样的代码: myRepository.GetAllContacts().ToLookup( c => c.COMPANY_ID); 我希望有一个类似的结构: new ConcurrentDicitonary<String,IEnumerable<Contact>>(); // <Company,List of Contacts in company> newconcurrentdicitory();//
ToLookup()
并发?我有一个类似这样的代码:
myRepository.GetAllContacts().ToLookup( c => c.COMPANY_ID);
我希望有一个类似的结构:
new ConcurrentDicitonary<String,IEnumerable<Contact>>(); // <Company,List of Contacts in company>
newconcurrentdicitory();//
因为每个
公司ID
都可以映射到多个联系人
,所以我不能简单地使用进行编辑
,这似乎是一个简单的问题,但正如其他(有问题的)答案所示,解决方案其实并不简单
现有答案的问题
目前,这两种建议的解决方案都会导致创建某种字典,其中每次在任何给定键处枚举IEnumerable
,都会通过枚举和过滤原始集合从头开始重新创建过滤后的IEnumerable
。本质上,您在字典中存储的是获取所需的过滤联系人
集合的逻辑,而不是实际集合
因此,您将反复枚举原始的IEnumerable
。从线程安全的角度来看,这是危险的,即使它有效——这样做没有好处,只有开销
建议的解决方案
您是对的,Lookup/ILookup
的最佳现成线程安全替代方案似乎是ConcurrentDictionary
,其中TValue
源自IEnumerable
。它提供了查找功能的超集,如果您正确构建它,它是线程安全的。基类库中没有用于此的现成扩展方法,因此您只需滚动自己的实现:
IEnumerable<Contact> contacts = GetAllContacts();
ConcurrentDictionary<string, IReadOnlyList<Contact>> dict = new ConcurrentDictionary<string, IReadOnlyList<Contact>>();
foreach (IGrouping<string, Contact> group in contacts.GroupBy(c => c.COMPANY_ID))
{
if (!dict.TryAdd(group.Key, group.ToArray())) {
throw new InvalidOperationException("Key already added.");
}
}
IEnumerable contacts=GetAllContacts();
ConcurrentDictionary dict=新建ConcurrentDictionary();
foreach(在contacts.GroupBy(c=>c.COMPANY\u ID)中对组进行分组)
{
如果(!dict.TryAdd(group.Key,group.ToArray())){
抛出新的InvalidOperationException(“已添加密钥”);
}
}
这看起来与其他人提供的非常相似,但有一个重要的区别:我的字典的TValue
是一个具体的集合(特别是Contact[]
冒充IReadOnlyList
)。它不会在每次从字典中取出并枚举它时从头开始重建
哦,而且我只列举了一次源代码IEnumerable
,从来都没有——不是真正改变生活,而是一次很好的接触
您仍然可以使用ConcurrentDictionary
作为您的字典类型(您可以在上面的示例中替换字典类型,它仍将按照预期编译和工作)-只需确保在构建字典时只向字典添加具体化的集合,最好是不可变的集合
选择您的t值
类型:IReadOnlyList的备选方案
(超出原问题的范围)
IReadOnlyList
是我能想到的最通用的准不可变集合接口(显然除了IReadOnlyCollection
之外),它向调用者传达集合已经实现,将来不太可能更改
如果我在自己的代码中使用它,我实际上会使用Contact[]
作为字典的TValue
用于任何私人和内部调用(出于性能原因放弃“只读”的舒适性)。对于任何公共API,我都会坚持使用IReadOnlyList
或可能的ReadOnlyCollection
,以强调TValue
集合的只读方面
如果采用外部依赖关系是一种可行的选择,您还可以将Microsoft的System.Collections.Immutable
NuGet添加到您的项目中,并使用ImmutableDictionary
存储查找ImmutableDictionary
是一个不可变的线程安全字典ImmutableArray
是一种轻量级数组包装器,它具有强大的不变性保证,并且通过结构枚举器和某些LINQ方法的重新实现(这些方法完全避免了枚举器分配)实现了可靠的性能特征
List
对于TValue
来说是一个糟糕的选择,因为它是a)可变的,b)它倾向于分配长度大于List.Count
(除非您明确使用List.trimOverse
)。当你把东西藏在字典里时,它很有可能会存活一段时间,所以分配你不打算使用的内存(比如List
does)不是一个好主意
编辑
现在,在所有这些之后,我必须添加:.NET当前的
查找实现由LINQ的ToLookup
返回,实际上看起来是线程安全的。但是,我发现的规范中没有一个对查找
上的实例方法的线程安全性做出任何保证(MSDN特别声明它们不保证线程安全),这意味着查找线程安全性是一个实现细节,而不是防弹保证。因此,我上面所说的重新使用ConcurrentDictionary仍然适用。这似乎是一个简单的问题,但正如其他(有问题的)答案所显示的那样,解决方案并非微不足道
现有答案的问题
目前,这两种建议的解决方案都会导致创建某种字典,其中每次在任何给定键处枚举IEnumerable
,都会通过枚举和过滤原始集合从头开始重新创建过滤后的IEnumerable
。本质上,您在字典中存储的是获取所需的过滤联系人
集合的逻辑,而不是实际集合
因此,您将反复枚举原始的IEnumerable
。从线程安全的角度来看,这是危险的,即使它有效,也没有b