方法: スレッド ローカル変数を使用する Parallel.For ループを記述するHow to: Write a Parallel.For Loop with Thread-Local Variables

次の例に、For ループによって生成される個別のタスクごとの状態を、スレッド ローカル変数を使用して格納および取得する方法を示します。This example shows how to use thread-local variables to store and retrieve state in each separate task that is created by a For loop. スレッド ローカル変数を使用することで、共有状態への多数のアクセスを同期するオーバーヘッドを回避できます。By using thread-local data, you can avoid the overhead of synchronizing a large number of accesses to shared state. 反復処理ごとに共有リソースを作成する代わりに、タスクの反復処理のすべてが完了するまで、値を計算して格納します。Instead of writing to a shared resource on each iteration, you compute and store the value until all iterations for the task are complete. この場合、最終結果を共有リソースに 1 回書き込んだり、別のメソッドに渡したりすることができます。You can then write the final result once to the shared resource, or pass it to another method.

Example

For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) メソッドを呼び出して、100 万個の要素からなる配列の値の合計を計算する例を次に示します。The following example calls the For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) method to calculate the sum of the values in an array that contains one million elements. 各要素の値は、そのインデックスに相当します。The value of each element is equal to its index.

using System;
using System.Collections.Generic;
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;

        // Use type parameter to make subtotal a long, not an int
        Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
        {
            subtotal += nums[j];
            return subtotal;
        },
            (x) => Interlocked.Add(ref total, x)
        );

        Console.WriteLine("The total is {0:N0}", total);
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}
'How to: Write a Parallel.For Loop That Has Thread-Local Variables

Imports System.Threading
Imports System.Threading.Tasks

Module ForWithThreadLocal

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

        ' Use type parameter to make subtotal a Long type. Function will overflow otherwise.
        Parallel.For(Of Long)(0, nums.Length, Function() 0, Function(j, [loop], subtotal)
                                                                subtotal += nums(j)
                                                                Return subtotal
                                                            End Function, Function(x) Interlocked.Add(total, x))

        Console.WriteLine("The total is {0:N0}", total)
        Console.WriteLine("Press any key to exit")
        Console.ReadKey()
    End Sub

End Module

すべての For メソッドで、最初の 2 つのパラメーターが最初と最後の反復値を指定します。The first two parameters of every For method specify the beginning and ending iteration values. メソッドのこのオーバーロードでは、3 番目のパラメーターでローカル状態を初期化します。In this overload of the method, the third parameter is where you initialize your local state. このコンテキストでのローカル状態は、現在のスレッドで実行されるループの最初の反復処理の直前から最後の反復処理の直後までの有効期限を持つ変数を意味します。In this context, local state means a variable whose lifetime extends from just before the first iteration of the loop on the current thread, to just after the last iteration.

3 番目のパラメーターの型は Func<TResult> です。ここで、TResult はスレッド ローカル状態を格納する変数の型です。The type of the third parameter is a Func<TResult> where TResult is the type of the variable that will store the thread-local state. この型は、ジェネリックの For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) メソッドの呼び出し時に提供されるジェネリック型引数によって定義されます (この例では、Int64)。Its type is defined by the generic type argument supplied when calling the generic For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) method, which in this case is Int64. 型引数は、コンパイラに対し、スレッド ローカル状態を格納するために使用する一時変数の型を指定します。The type argument tells the compiler the type of the temporary variable that will be used to store the thread-local state. この例では、式 () => 0 (Visual Basic の場合は Function() 0) でスレッド ローカル変数をゼロに初期化します。In this example, the expression () => 0 (or Function() 0 in Visual Basic) initializes the thread-local variable to zero. ジェネリック型引数が参照型またはユーザー定義の値型である場合は、以下のような式になります。If the generic type argument is a reference type or user-defined value type, the expression would look like this:

() => new MyClass()  
Function() new MyClass()  

4 番目のパラメーターは、ループのロジックを定義します。The fourth parameter defines the loop logic. パラメーター値は、シグネチャが Func<int, ParallelLoopState, long, long> (C# の場合) または Func(Of Integer, ParallelLoopState, Long, Long) (Visual Basic の場合) となっているデリゲートまたはラムダ式でなければなりません。It must be a delegate or lambda expression whose signature is Func<int, ParallelLoopState, long, long> in C# or Func(Of Integer, ParallelLoopState, Long, Long) in Visual Basic. 最初のパラメーターは、ループのその特定の反復処理に対するループ カウンターの値です。The first parameter is the value of the loop counter for that iteration of the loop. 2 番目のパラメーターは、ループを抜けるために使用できる ParallelLoopState オブジェクトです。このオブジェクトは、Parallel クラスによって各ループの発生時に提供されます。The second is a ParallelLoopState object that can be used to break out of the loop; this object is provided by the Parallel class to each occurrence of the loop. 3 番目のパラメーターは、スレッド ローカル変数です。The third parameter is the thread-local variable. 最後のパラメーターは、戻り値の型です。The last parameter is the return type. この例の場合、型は Int64 型引数で指定されているため、For になります。In this case, the type is Int64 because that is the type we specified in the For type argument. その変数の名前は subtotal です。これは、ラムダ式によって返されます。That variable is named subtotal and is returned by the lambda expression. この戻り値が、ループの次の反復処理で subtotal を初期化するために使用されます。The return value is used to initialize subtotal on each subsequent iteration of the loop. この最後のパラメーターは、各反復処理に渡されて、最後の反復処理が完了した時点で localFinally デリゲートに渡される値であるとも考えられます。You can also think of this last parameter as a value that is passed to each iteration, and then passed to the localFinally delegate when the last iteration is complete.

5 番目のパラメーターが定義するメソッドは、特定のスレッドでのすべての反復処理が完了した時点で 1 回だけ呼び出されます。The fifth parameter defines the method that is called once, after all the iterations on a particular thread have completed. この場合も、入力引数の型は For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) メソッドの型引数、および本体のラムダ式によって返される型と一致します。The type of the input argument again corresponds to the type argument of the For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) method and the type returned by the body lambda expression. この例では、スレッド セーフな方法で、この値をクラス スコープで変数に追加するために、Interlocked.Add メソッドを呼び出します。In this example, the value is added to a variable at class scope in a thread safe way by calling the Interlocked.Add method. スレッド ローカル変数を使用することで、このクラス変数をループのすべての反復処理で作成する手間を省きました。By using a thread-local variable, we have avoided writing to this class variable on every iteration of the loop.

ラムダ式の使用方法の詳細については、「PLINQ および TPL のラムダ式」を参照してください。For more information about how to use lambda expressions, see Lambda Expressions in PLINQ and TPL.

関連項目See also