根据讨论here ,在互联网上的某个地方,已验证在枚举某些类型的集合时替换它们是可能的/线程安全的。
我的下面的测试似乎证实了这一点。
// This test confirmed insufficient by comments
var a = new List<int> { 1, 2, 3 };
Parallel.For(1, 10000, i => {
foreach (var x in a)
Console.WriteLine(i + x);
});
Parallel.For(1, 10000, i => a = new List<int> { 1, 2, 3, 4 });
但是,在我开始在代码中实现它之前,我非常想阅读一些官方文档或与此事实相关的一些具体引用资料。
有人可以验证这一点/发布链接吗?
最佳答案
正如已经提到的,你实际上并没有变异 a
当你迭代它的时候。你迭代了一堆,然后在完成一堆迭代后,你正在变异 a
一堆,因为Parallel.For
将阻塞,直到完成所有迭代的执行。
但是,即使你正在变异a
与这里的迭代并行,它实际上是完全安全的。 foreach
将读取 a
的值从一开始一次,获取对列表的引用,然后从那时起,它永远不会查看 a
再次。它将使用本地副本来引用从 a
获得的列表。 ,因此它不会知道或关心对变量 a
进行了哪些更改在那之后。因此,如果您要改变列表 a
指向并迭代a
同时,你也不知道正在迭代的列表是否是 a
中的列表。在另一个线程中进行更改之前或之后,但您知道正在迭代的列表必须是一个列表或另一个列表,而不是某种错误或两者的混合。
现在,如果您要改变列表 a
引用而不是改变变量 a
指向一个新的引用,那就完全不同了。 List
并非设计为同时从多个线程访问,因此会发生各种不好的事情。如果您使用专门设计用于从多个线程访问的集合,并且按照设计的方式使用它,那么它可以正常运行。
关于C# 枚举时替换集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41398708/