Export (0) Print
Expand All

How to: Write a Parallel.ForEach Loop That Has Thread-Local Variables

The following example shows how to write a ForEach method that uses thread-local variables. When a ForEach loop executes, it divides its source collection into multiple partitions. Each partition will get its own copy of the "thread-local" variable. (The term "thread-local" is slightly inaccurate here, because in some cases two partitions may run on the same thread.)

The code and parameters in this example closely resemble the corresponding For method. For more information, see How to: Write a Parallel.For Loop That Has Thread-Local Variables.

To use a thread-local variable in a ForEach loop, you must use the version of the method that takes two type parameters. The first parameter specifies the type of the source element and the second parameter specifies the type of the thread-local variable.

The first input parameter is the data source, and the second is the function that will initialize the thread-local variable. The third input parameter is a Func<T1, T2, T3, TResult> that is invoked by the parallel loop on each iteration. You supply the code for the delegate, and the loop passes in the input parameters. The input parameters are the current element, a ParallelLoopState variable that lets you examine the state of the loop, and the thread-local variable. You return the thread-local variable, and the method then passes it to the next iteration on this partition. This variable is distinct on all loop partitions.

The last input parameter of the ForEach method is the Action<T> delegate that the method will invoke when all loops have completed. The method supplies the final value of the thread-local variable for this thread (or loop partition), and you provide the code that captures the final value and performs whatever action is required to combine the result from this partition with the results from the other partitions. Because the delegate type is an Action<T>, there is no return value.


namespace ThreadLocalForEach
{
    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 local data (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 all loops have completed.
                // finalResult is the final value of subtotal. supplied by the ForEach method.
                                        (finalResult) => Interlocked.Add(ref total, finalResult)
                                        );

            Console.WriteLine("The total from Parallel.ForEach is {0}", total);
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }
}


Community Additions

ADD
Show:
© 2014 Microsoft