这是使用Parallel.ForEach()线程安全吗?

基本上,我正在使用这个:

var data = input.AsParallel(); List output = new List(); Parallel.ForEach(data, line => { String outputLine = ""; // ** Do something with "line" and store result in "outputLine" ** // Additionally, there are some this.Invoke statements for updating UI output.Add(outputLine); }); 

输入是List对象。 ForEach()语句对每个值执行一些处理,更新UI,并将结果添加到output List 。 这有什么本质上的错误吗?

笔记:

  • 输出顺序并不重要

更新:

根据我得到的反馈,我在output.Add语句以及UI更新代码中添加了手动lock

是; List不是线程安全的,因此从任意线程(很可能同时)ad-hoc添加它是注定的。 您应该使用线程安全列表,或手动添加锁定。 或者也许有一个Parallel.ToList

此外,如果重要:不保证插入订单。

但这个版本安全的:

 var output = new string[data.Count]; Parallel.ForEach(data, (line,state,index) => { String outputLine = index.ToString(); // ** Do something with "line" and store result in "outputLine" ** // Additionally, there are some this.Invoke statements for updating UI output[index] = outputLine; }); 

这里我们使用index来更新每个并行调用的不同数组索引。

这有什么本质上的错误吗?

是的,一切。 这些都不安全。 列表并不安全地同时更新多个线程,并且您无法从UI线程以外的任何线程更新UI。

文档说明了以下关于List的线程安全性:

此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。 任何实例成员都不保证是线程安全的。

List(Of T)可以同时支持多个读者,只要不修改集合即可。 枚举集合本质上不是线程安全的过程。 在枚举与一个或多个写访问争用的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。 要允许多个线程访问集合以进行读取和写入,您必须实现自己的同步。

因此, output.Add(outputLine) 不是线程安全的,您需要自己确保线程安全,例如,通过将add操作包装在lock语句中。