基本上,我正在处理这个问题:
var data = input.AsParallel();
List<String> output = new List<String>();
Parallel.ForEach<String>(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<String>
个对象。 ForEach()
语句对每个值执行一些处理,更新UI,并将结果添加到output
List
。这有什么本质上的错误吗?
备注:
更新
根据我收到的反馈,我在lock
语句以及UI更新代码中添加了手册output.Add
。
答案 0 :(得分:27)
是; List<T>
不是线程安全的,因此从任意线程(很可能同时)ad-hoc添加它是注定要失败的。您应该使用线程安全列表,或手动添加锁定。或者可能有Parallel.ToList
。
此外,如果重要:无法保证插入订单。
此版本 是安全的,但是:
var output = new string[data.Count];
Parallel.ForEach<String>(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
来更新每个并行调用的不同数组索引。
答案 1 :(得分:10)
这有什么本质上的错误吗?
是的,一切。这些都不安全。列表并不安全地同时更新多个线程,并且您无法从UI线程以外的任何线程更新UI。
答案 2 :(得分:3)
The documentation说明了List<T>
的线程安全性:
此类型的公共静态(在Visual Basic中为Shared)成员是线程安全的。不保证任何实例成员都是线程安全的。
List(Of T)可以同时支持多个读者,只要不修改集合即可。枚举通过集合本质上不是线程安全的过程。在枚举与一个或多个写访问争用的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。
因此,output.Add(outputLine)
不是线程安全的,您需要自己确保线程安全,例如,通过将add操作包装在lock
语句中。