共用方式為


如何:使用 partition-local 變數撰寫 Parallel.ForEach 迴圈

下列範例示範如何撰寫 ForEach 使用分割區局部變數的方法。 ForEach當迴圈執行時,它會將其來源集合分割成多個分割區。 每個分割區都有自己的區域變數副本。 數據分割局部 變數類似於線程局部變數,不同之處在於多個分割區可以在單一線程上執行。

此範例中的程式代碼和參數與對應的 For 方法非常類似。 如需詳細資訊,請參閱 如何:使用 Thread-Local 變數撰寫 Parallel.For 迴圈

若要在 ForEach 迴圈中使用分割區域變數,您必須呼叫具備兩個類型參數的方法多載之一。 第一個型別參數 TSource會指定來源專案的型別,而第二個型別參數 TLocal則指定 partition-local 變數的類型。

範例

下列範例會呼叫 Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) 多載來計算一百萬個元素的陣列總和。 此多載有四個參數:

  • source,這是數據源。 它必須實作 IEnumerable<T>。 我們範例中的數據源是由Enumerable.Range方法所傳回的包含一百萬個成員的IEnumerable<Int32>物件。

  • localInit或初始化分區局部變數的函式。 此函式會針對作業執行的每個分割 Parallel.ForEach 區呼叫一次。 我們的範例會將分割區局部變數初始化為零。

  • body Func<T1,T2,T3,TResult> 是在循環的每次迭代中由並行循環調用的。 其簽章為 Func\<TSource, ParallelLoopState, TLocal, TLocal>。 您提供委派方法的程式碼,然後迴圈會傳入輸入參數,也就是:

    • 當前的元素 IEnumerable<T>

    • ParallelLoopState一個您可以在委派者的程式碼中使用的變數,用來檢查迴圈的狀態。

    • 分區區域變數。

    您的委派會傳回 partition-local 的變數,然後傳遞給該特定分割區中執行的迴圈的下一次反覆運算。 每個迴圈分割區都會維護這個變數的個別實例。

    在此範例中,委派會將每個整數的值新增至 partition-local 變數,這會維護該分割區中整數元素值的執行總計。

  • localFinally,當 Action<TLocal> 每個分割區中的迴圈作業完成時, Parallel.ForEach 叫用的委派。 方法 Parallel.ForEach 會將此 Action<TLocal> 委派分割區的區域變數的最終值傳遞給您,同時您提供程式碼來執行將此分割區的結果與其他分割區結果結合所需的動作。 此代理可由多個工作同時調用。 因此,此範例會使用 Interlocked.Add(Int32, Int32) 方法來同步存取 total 變數。 因為委派類型是 Action<T>,因此沒有傳回值。

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    static void Main()
    {
        int[] nums = Enumerable.Range(0, 1000000).ToArray();
        long total = 0;

        // First type parameter is the type of the source elements
        // Second type parameter is the type of the thread-local variable (partition subtotal)
        Parallel.ForEach<int, long>(
            nums, // source collection
            () => 0, // method to initialize the local variable
            (j, loop, subtotal) => // method invoked by the loop on each iteration
            {
                subtotal += j; //modify local variable
                return subtotal; // value to be passed to next iteration
            },
            // Method to be executed when each partition has completed.
            // finalResult is the final value of subtotal for a particular partition.
            (finalResult) => Interlocked.Add(ref total, finalResult));

        Console.WriteLine($"The total from Parallel.ForEach is {total:N0}");
    }
}
// The example displays the following output:
//        The total from Parallel.ForEach is 499,999,500,000
' How to: Write a Parallel.ForEach Loop That Has Thread-Local Variables

Imports System.Threading
Imports System.Threading.Tasks

Module ForEachThreadLocal
    Sub Main()

        Dim nums() As Integer = Enumerable.Range(0, 1000000).ToArray()
        Dim total As Long = 0

        ' First type parameter is the type of the source elements
        ' Second type parameter is the type of the thread-local variable (partition subtotal)
        Parallel.ForEach(Of Integer, Long)(nums, Function() 0,
                                           Function(elem, loopState, subtotal)
                                               subtotal += elem
                                               Return subtotal
                                           End Function,
                                            Sub(finalResult)
                                                Interlocked.Add(total, finalResult)
                                            End Sub)

        Console.WriteLine("The result of Parallel.ForEach is {0:N0}", total)
    End Sub
End Module
' The example displays the following output:
'       The result of Parallel.ForEach is 499,999,500,000

另請參閱