方法: パーティション ローカル変数を使用する Parallel.ForEach ループを記述する

パーティション ローカル変数を使用する ForEach メソッドを記述する方法を次の例に示します。 ForEach ループが実行されると、そのソース コレクションが複数のパーティションに分割されます。 各パーティションは、パーティション ローカル変数の独自のコピーを所有しています。 パーティション ローカル変数は、1 つのスレッドに対して複数のパーティションを実行できる点を除き、スレッド ローカル変数と似ています。

この例のコードおよびパラメーターは、対応する For メソッドによく似ています。 詳細については、「方法:スレッド ローカル変数を使用する Parallel.For ループを記述する」を参照してください。

ForEach ループでパーティション ローカル変数を使用するには、2 つのタイプのパラメーターを取るメソッド オーバーロードのうち、いずれか 1 つを呼び出す必要があります。 最初の型パラメーター TSource でソース要素の型を指定し、2 番目の型パラメーター TLocal でパーティション ローカル変数の型を指定します。

以下の例では、Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) のオーバーロードを呼び出して、100 万個の要素からなる配列の合計を計算します。 このオーバーロードには、4 つのパラメータがあります。

  • source。データ ソースです。 これは、IEnumerable<T> を実装する必要があります。 この例のデータ ソースは、IEnumerable<Int32> によって返される、100 万個のメンバーからなる Enumerable.Range オブジェクトです。

  • localInit。またはパーティション ローカル変数を初期化する関数です。 この関数は、Parallel.ForEach 操作が実行される各パーティションで 1 回呼び出されます。 この例では、パーティション ローカル変数がゼロに初期化されます。

  • body。ループの各反復処理に対して並列ループで呼び出される Func<T1,T2,T3,TResult> です。 シグニチャは Func\<TSource, ParallelLoopState, TLocal, TLocal> です。 プログラマはデリゲートのコードを作成します。入力パラメーターはループから渡されます。これらの入力パラメーターは以下のとおりです。

    • IEnumerable<T> の現在の要素。

    • ParallelLoopState 変数。デリゲートのコードで、ループの状態を確認するために使用できます。

    • パーティション ローカル変数。

    デリゲートからパーティション ローカル変数が返されると、その変数は、その特定のパーティションで実行されるループの次の反復処理に渡されます。 ループ パーティションごとに、この変数の個別のインスタンスが保持されます。

    この例では、デリゲートによって個々の整数の値がパーティション ローカル変数に追加されます。スレッド ローカル変数には、そのパーティションで順次追加される整数要素の値の現在の合計が保持されます。

  • localFinally。各パーティションでのループ操作が完了した時点で、Action<TLocal> によって呼び出される Parallel.ForEach デリゲート。 Parallel.ForEach メソッドは、Action<TLocal> デリゲートに、このループ パーティションのパーティション ローカル変数の最終値を返します。プログラマは、このパーティションの結果と他のパーティションの結果を結合するために必要な操作を実行するコードを作成します。 このデリゲートは、複数のタスクで同時に呼び出すことができます。 このため、この例では total 変数へのアクセスを同期するために Interlocked.Add(Int32, Int32) メソッドが使用されます。 デリゲート型は 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 {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 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

関連項目