我有一个像这样的ConcurrentDictionary:
ConcurrentDictionary<int, Dto> concurrentDictionary = new ConcurrentDictionary<int, Dto>();
这是可读写的字典,可供许多线程使用。我可以以线程安全的方式管理可写端。当我需要在字典中访问Dto时,我总是使用Linq select方法来获取Dto的。
IEnumerable<Dto> dtos = concurrentDictionary.Select(p => p.Value);
我知道,并发字典上的Linq方法对于可读写的并发字典是无锁和线程安全的。在使用Linq访问Dto之后,使用这些Dto上的一些读取函数是否可以安全线程?我的意思是在foreach循环中访问这些Dto,或者从它们创建新列表并对这些Dto的属性进行过滤或排序?
ConcurrentDictionary<int, Dto> concurrentDictionary = new ConcurrentDictionary<int, Dto>();
for (int i = 0; i < 10000; i++)
{
concurrentDictionary.TryAdd(i, new Dto()
{ Id = i, Name = RandomString(35), Type = "new", IsActive = true} );
}
IEnumerable<Dto> dtos = concurrentDictionary.Select(p => p.Value);
ArrayList arrayList = new ArrayList();
foreach (object obj in dtos)
{
//is it thread safe accessing the Dto like this and adding them to new arraylist?
arrayList.Add(obj);
}
//Is it thread safe accessing the Dto's properties like this?
//Of course only for reading purposes.
string name = ((Dto) arrayList[0]).Name;
答案 0 :(得分:1)
根据GetEnumerator ConcurrentDictionary的文档(突出显示是我的):
从字典返回的枚举器可以安全使用 同时读取和写入字典,但确实如此 不代表字典的即时快照。该 通过枚举器公开的内容可能包含所做的修改 在调用GetEnumerator之后到字典。
您是否认为此线程安全取决于您需要的语义。请注意,如果您执行想要快照,则可以通过调用ToArray()
方法来实现此目的:
IEnumerable<Dto> dtos = concurrentDictionary.ToArray().Select(p => p.Value);
根据documentation,ToArray()
返回:
包含从中复制的键和值对的快照的新数组 System.Collections.Concurrent.ConcurrentDictionary。
(请注意,这是属于ToArray
的{{1}}方法,而不是Linq ConcurrentDictionary
方法,它将使用枚举器。)
最后,请注意,正如已经指出的那样,如果你的DTO是可变的,那么所有的赌注都会被取消。您可能会获得列表的快照,但没有什么可以防止该列表的内容在另一个线程上被更改。
修改强>
通过阅读评论中链接到的page,接口点是:
ConcurrentDictionary的所有公共成员和受保护成员 是线程安全的,可以从多个线程同时使用。 但是,成员通过其中一个接口访问了 ConcurrentDictionary实现,包括扩展 方法,不保证是线程安全的,可能需要 由来电者同步。
这告诉我们,当与ToArray()
一起使用时,Linq不保证是线程安全的。 ConcurrentDictionary
应该没问题,因为这会调用for each
,其中包含本帖开头提到的保证。我的预感是GetEnumerator
也没关系,但不能保证。{/ p>
所以回答代码中的问题:
Select
可能,但不保证,无论如何,这对你没有任何好处IEnumerable<Dto> dtos = concurrentDictionary.Select(p => p.Value);
ArrayList arrayList = new ArrayList();
foreach (object obj in dtos)
{
//is it thread safe accessing the Dto like this and adding them to new arraylist?
arrayList.Add(obj);
}
,所以我会用以下内容替换所有内容:
ToArray()
其次:
KeyValuePair<int, Dto>[] dtoArray = concurrentDictionary.ToArray();
这是否安全与//Is it thread safe accessing the Dto's properties like this?
//Of course only for reading purposes.
string name = ((Dto) arrayList[0]).Name;
无关,而与ConcurrentDictionary
中Name
方法的实施有关。是的,您可以安全地访问Dto
,但您访问的任何属性或方法都需要是线程安全的。如果您使用了上面建议的Dto
,那么
ToArray()
是线程安全的,但是
Dto dto = dtoArray[0].Value
取决于string name = dtoArray[0].Value.Name
的实施。