C#枚举时替换集合

C#枚举时替换集合,c#,multithreading,C#,Multithreading,根据讨论,在internet上的某处某处已验证在枚举某些类型的集合时替换它们是可能的/线程安全的 我下面的测试似乎证实了这一点 //此测试通过注释确认不足 var a=新列表{1,2,3}; 对于(11000,i=>{ foreach(a中的变量x) 控制台写入线(i+x); }); Parallel.For(1,10000,i=>a=新列表{1,2,3,4}); 然而,在我开始在代码中实现它之前,我非常希望阅读一些官方文档或一些与此相关的具体参考资料。 有人可以验证这个/发布一个链接吗?只是

根据讨论,在internet上的某处某处已验证在枚举某些类型的集合时替换它们是可能的/线程安全的

我下面的测试似乎证实了这一点

//此测试通过注释确认不足
var a=新列表{1,2,3};
对于(11000,i=>{
foreach(a中的变量x)
控制台写入线(i+x);
});
Parallel.For(1,10000,i=>a=新列表{1,2,3,4});
然而,在我开始在代码中实现它之前,我非常希望阅读一些官方文档或一些与此相关的具体参考资料。


有人可以验证这个/发布一个链接吗?

只是为了补充Servy的答案和评论中所说的,您所拥有的并不是在迭代过程中并行修改变量的示例。您的
并行。For
循环按顺序运行-即,首先在列表上迭代10000次(可能并行),然后用新列表替换它10000次(同样,可能并行)

//这根本不会修改或替换集合,它只是在集合上迭代多次
对于(11000,i=>{
foreach(a中的变量x)
控制台写入线(i+x);
});
//这发生在上一个Parallel.For循环完成之后
//因此,在这一点上,您实际上并没有迭代循环,只是多次替换它
Parallel.For(1,10000,i=>a=新列表{1,2,3,4});
请注意,我说的可能是并行的-简单地将某个东西放在
并行中。For
循环不能保证框架将实际使用多个线程来完成任务,并且您无法“提前”预测如果它这样做,它将使用多少线程。重点是,这段代码甚至不一定证明这些任务在多个线程上运行(或者如果它们在多个线程上运行,则证明它们在多个线程上运行)

此测试中的另一个缺陷是:每次都使用相同的确切集合替换类,因此无法真正判断循环完成后哪个线程进行了最终更新。假设它使用3个不同的线程来执行此操作—A、B和C。您如何知道是哪一个线程对集合进行了最后一次更新?回想一下,
Parallel.For
循环不能保证按顺序执行,因此它可以由这三个循环中的任何一个进行更新。从(我的)重点:

并行循环的语法与for和foreach非常相似 循环您已经知道了,但是并行循环在计算机上运行得更快 具有可用内核的计算机另一个区别是 对于顺序循环,并行循环的执行顺序没有定义 循环。步骤通常同时并行进行有时, 两个步骤的发生顺序与循环发生时的顺序相反 是连续的。唯一的保证是所有循环的 迭代将在循环完成时运行


基本上,对于并行。对于循环,您“事先”不知道并行度,它是否使用并行,甚至不知道步骤将按什么顺序执行(因此,使用此结构必然会放弃对代码实际执行方式的大量控制)。

如前所述,实际上,在迭代过程中,您并没有对
a
进行变异。你迭代了一堆,然后在迭代了一堆之后,你就变异了
a
a堆,因为
Parallel.For
将阻塞,直到它完成所有的迭代

但是,即使您在这里与迭代并行地变异
a
,它实际上也是完全安全的。
foreach
从一开始就要读取
a
的值一次,获取一个列表的引用,然后从那以后,它再也不会查看
a
。它将从
a
中获取列表引用的本地副本,因此它不知道或不关心在该点之后对变量
a
所做的更改。因此,如果您正在对列表
a
指向的内容进行变异,同时也在迭代
a
,那么您不知道所迭代的列表是在另一个线程中更改之前还是之后的列表,但您知道所迭代的列表必须是一个列表或另一个列表,而不是一些错误或两者的混合


现在,如果您对
a
引用的列表进行变异,而不是将变量
a
变异为指向新引用,那么这将是完全不同的<代码>列表不是设计为同时从多个线程访问的,因此会发生各种不好的事情。如果您使用了一个专门设计用于从多个线程访问的集合,并且以设计用于使用的方式使用它,那么它就可以正常工作。

两个并行的.For是按顺序执行的。Parallel.For仅在所有迭代完成时终止。因此,在枚举集合时不会替换集合。在测试中被替换时不会迭代任何集合。For并行执行迭代1到1000,并且仅在所有999次迭代完成时返回。如果您在“列表a”进行迭代时弄乱了它,foreach会非常讨厌您。OP是对保存集合的变量进行变异,而不是对正在迭代的集合进行变异,因此这不适用于这种情况。@Servy我的编辑解决了这个问题吗?但是,从我的角度来看,他是否在修改集合并不重要,因为他实际上并没有在第二个循环中迭代列表
// 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 });
// This doesn't modify or replace the collection at all, it just iterates over it a bunch of times
Parallel.For(1, 10000, i => {
    foreach (var x in a)
        Console.WriteLine(i + x);
});

// This happens AFTER the previous Parallel.For loop completes
// Thus, you're not actually iterating over the loop at this point, just replacing it a bunch of times
Parallel.For(1, 10000, i => a = new List<int> { 1, 2, 3, 4 });