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

时间:2011-04-09 13:49:27

标签: c# multithreading thread-safety parallel-processing

基本上,我正在处理这个问题:

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

3 个答案:

答案 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语句中。

相关问题