如何:撰寫含有分割區域變數的 Parallel.ForEach 迴圈How to: Write a Parallel.ForEach loop with partition-local variables

下列範例說明如何撰寫使用分割區域變數的 ForEach 方法。The following example shows how to write a ForEach method that uses partition-local variables. ForEach 迴圈執行時,它會將其來源集合分成多個分割。When a ForEach loop executes, it divides its source collection into multiple partitions. 每個分割會有自己的分割區域變數複本。Each partition has its own copy of the partition-local variable. 分割區域變數與執行緒區域變數類似,不同之處在於多個分割可以在單一執行緒上執行。A partition-local variable is similar to a thread-local variable, except that multiple partitions can run on a single thread.

這個範例中的程式碼和參數非常類似於對應的 For 方法。The code and parameters in this example closely resemble the corresponding For method. 如需詳細資訊,請參閱:如何:撰寫含有執行緒區域變數的 Parallel.For 迴圈For more information, see How to: Write a Parallel.For Loop with Thread-Local Variables.

若要在 ForEach 迴圈中使用分割區域變數,您必須呼叫使用兩個型別參數的其中一個方法多載。To use a partition-local variable in a ForEach loop, you must call one of the method overloads that takes two type parameters. 第一個型別參數 TSource 指定來源項目的類型,而第二個型別參數 TLocal 則指定分割區域變數的類型。The first type parameter, TSource, specifies the type of the source element, and the second type parameter, TLocal, specifies the type of the partition-local variable.

範例Example

下列範例呼叫 Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) 多載,以計算一百萬個項目之陣列的總和。The following example calls the Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) overload to compute the sum of an array of one million elements. 此多載包含四個參數:This overload has four parameters:

  • source,這是資料來源。source, which is the data source. 它必須實作 IEnumerable<T>It must implement IEnumerable<T>. 在我們的範例中,資料來源是 IEnumerable<Int32> 方法傳回的一百萬個成員 Enumerable.Range 物件。The data source in our example is the one million member IEnumerable<Int32> object returned by the Enumerable.Range method.

  • localInit,或初始化分割區域變數的函式。localInit, or the function that initializes the partition-local variable. 此函式會針對 Parallel.ForEach 作業執行所在的每個分割呼叫一次。This function is called once for each partition in which the Parallel.ForEach operation executes. 我們的範例會將分割區域變數初始化為零。Our example initializes the partition-local variable to zero.

  • body,這是平行迴圈在迴圈的每個反覆項目上叫用的 Func<T1,T2,T3,TResult>body, a Func<T1,T2,T3,TResult> that is invoked by the parallel loop on each iteration of the loop. 其簽章為 Func\<TSource, ParallelLoopState, TLocal, TLocal>Its signature is Func\<TSource, ParallelLoopState, TLocal, TLocal>. 您會提供委派的程式碼,而迴圈會傳入輸入參數,包括:You supply the code for the delegate, and the loop passes in the input parameters, which are:

    • IEnumerable<T> 的目前項目。The current element of the IEnumerable<T>.

    • 可用於委派的程式碼中以檢查迴圈狀態的 ParallelLoopState 變數。A ParallelLoopState variable that you can use in your delegate's code to examine the state of the loop.

    • 分割區域變數。The partition-local variable.

    您的委派會傳回分割區域變數,該變數接著會傳遞給該特定分割中所執行之迴圈的下一個反覆項目。Your delegate returns the partition-local variable, which is then passed to the next iteration of the loop that executes in that particular partition. 每個迴圈分割會保留此變數的個別執行個體。Each loop partition maintains a separate instance of this variable.

    在這個範例中,委派會將每個整數的值加入至分割區域變數,以維護該分割中整數值項目的累積總計。In the example, the delegate adds the value of each integer to the partition-local variable, which maintains a running total of the values of the integer elements in that partition.

  • localFinally,這是當每個分割的迴圈作業完成時,Action<TLocal> 所叫用的 Parallel.ForEach 委派。localFinally, an Action<TLocal> delegate that the Parallel.ForEach invokes when the looping operations in each partition have completed. Parallel.ForEach 方法會將此迴圈分割之分割區域變數的最後一個值傳遞給 Action<TLocal> 委派,而您會提供程式碼,以執行為了合併此分割中的結果與其餘分割中的結果所需的動作。The Parallel.ForEach method passes your Action<TLocal> delegate the final value of the partition-local variable for this loop partition, and you provide the code that performs the required action for combining the result from this partition with the results from the other partitions. 這個委派可由多個工作同時叫用。This delegate can be invoked concurrently by multiple tasks. 因此,此範例使用 Interlocked.Add(Int32, Int32) 方法來同步處理對 total 變數的存取。Because of this, the example uses the Interlocked.Add(Int32, Int32) method to synchronize access to the total variable. 由於委派類型是 Action<T>,因此不會有傳回值。Because the delegate type is an Action<T>, there is no return value.

 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 {0:N0}", total);
    }
}
// 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 paramemter 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

請參閱See also