簡體   English   中英

使用 Parallel.ForEach() 線程安全嗎?

[英]Is this use of Parallel.ForEach() thread safe?

本質上,我正在處理這個:

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> object。ForEach ForEach()語句對每個值進行一些處理,更新 UI,並將結果添加到output List中。 這有什么本質上的錯誤嗎?

筆記:

  • Output順序不重要

更新:

根據我收到的反饋,我在output.Add語句以及 UI 更新代碼中添加了手動lock

是; 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來更新每個並行調用的不同數組索引。

這有什么本質上的錯誤嗎?

是的,一切。 這些都不安全。 列表並不安全地同時更新多個線程,並且您無法從UI線程以外的任何線程更新UI。

文檔說明了以下關於List<T>的線程安全性:

此類型的公共靜態(在Visual Basic中為Shared)成員是線程安全的。 任何實例成員都不保證是線程安全的。

List(Of T)可以同時支持多個讀者,只要不修改集合即可。 枚舉通過集合本質上不是線程安全的過程。 在枚舉與一個或多個寫訪問爭用的極少數情況下,確保線程安全的唯一方法是在整個枚舉期間鎖定集合。 要允許多個線程訪問集合以進行讀寫,您必須實現自己的同步。

因此, output.Add(outputLine) 不是線程安全的,您需要自己確保線程安全,例如,通過將add操作包裝在lock語句中。

當您想要並行操作的結果時, PLINQParallel class 更方便。您通過將input轉換為ParallelQuery<T>開始得很好:

ParallelQuery<string> data = input.AsParallel();

...但隨后您將data提供給Parallel.ForEach ,它將其視為標准IEnumerable<T> 所以AsParallel()被浪費了。 它不提供任何並行化,只提供開銷。 下面是使用 PLINQ 的正確方法:

List<string> output = input
    .AsParallel()
    .Select(line =>
    {
        string outputLine = ""; 
        // ** Do something with "line" and store result in "outputLine" **
        return outputLine;
    })
    .ToList();

您應該記住的一些差異:

  1. Parallel默認在ThreadPool上運行代碼,但它是可配置的。 PLINQ 專門使用ThreadPool
  2. Parallel默認情況下具有無限並行性(它使用ThreadPool的所有可用線程)。 PLINQ 默認使用最多Environment.ProcessorCount線程。

關於結果的順序,默認情況下 PLINQ 不保留順序。 如果您想保留訂單,您可以附加AsOrdered運算符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM