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

Chr*_*nte 21 c# parallel-processing multithreading thread-safety

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

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);
});
Run Code Online (Sandbox Code Playgroud)

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

笔记:

  • 输出顺序并不重要

更新:

根据我得到的反馈,我lockoutput.Add声明中添加了一个手册,以及UI更新代码.

Mar*_*ell 30

是; 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;
});
Run Code Online (Sandbox Code Playgroud)

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

  • 还有`.AsParallel()`和ParallelEnumerable扩展方法`ToList()` (2认同)
  • 请注意,并行写入数组虽然线程安全,但不一定很快。当多个线程尝试写入驻留在同一内存块中的索引时,它们会相互等待。然而,我没有将它与线程安全集合进行比较——它肯定有相同的限制,因为这是低级的东西。 (2认同)

Eri*_*ert 11

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

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

  • @SimpleCoder - `lock`是多余的; 消息队列*是*线程安全的(它必须是,工作).所以你可以立即从任何地方"调用".添加一个`lock`是一个*潜在的*死锁源,所以我删除了那个. (2认同)

Hei*_*nzi 5

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

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

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

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