Поделиться через


Практическое руководство. Написание цикла Parallel.ForEach, содержащего локальные переменные потока

В следующем примере показано как написать метод ForEach, который использует локальные переменные потока. Когда выполняется цикл ForEachон разделяет свою исходную коллекцию на несколько разделов. Каждый раздел получит свою собственную копию локальной переменной потока. (термин "локальный для потока" в данном случае немного неточен, так как в некоторых случаях два раздела могут выполняться в одном потоке).

Коды и параметры в этом примере максимально приближена к соответствующему методу For. Дополнительные сведения см. в разделе Практическое руководство. Написание цикла Parallel.For, содержащего локальные переменные потока.

Пример

Чтобы использовать локальную переменную потока в цикле ForEach, необходимо использовать версию метода, который принимает два параметра type. Первый параметр задает тип исходного элемента, второй параметр задает тип локальной переменной потока.

Первый входной параметр является источником данных, второй параметр представляет функцию, которая будет инициализировать локальную переменную потока. Третий входной параметр — это функция Func<T1, T2, T3, TResult>, которая вызывается при каждой итерации параллельного цикла. Для делегата предоставляется код и цикл передается во входных параметрах. Входные параметры — это текущий элемент, переменная ParallelLoopState, которая позволяет оценить состояние цикла и локальную переменную потока. Возвращается локальная переменная потока и затем метод передает ее в следующую итерацию по этому разделу. Это переменная различается во всех разделах цикла.

Последний входной параметр метода ForEach является делегатом Action<T>, который метод будет вызывать, когда будут завершены все циклы. Этот метод предоставляет конечное значение локальной переменной потока для этого потока (или раздела цикла). Программист предоставляет код, который захватывает конечное значение и выполняет требуемое действие для объединения результата этого раздела с результатами других разделов. Так как этот делегат имеет тип Action<T>, возвращаемое значение отсутствует.

' 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 local data (subtotal)
        Parallel.ForEach(Of Integer, Long)(nums, Function() 0,
                                           Function(elem, loopState, subtotal)
                                               subtotal += nums(elem)
                                               Return subtotal
                                           End Function,
                                            Sub(finalResult)
                                                Interlocked.Add(total, finalResult)
                                            End Sub)

        Console.WriteLine("The result of Parallel.ForEach is {0}", total)
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub
End Module
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 += nums[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();
        }
    }
}

См. также

Задачи

Практическое руководство. Написание цикла Parallel.For, содержащего локальные переменные потока

Основные понятия

Параллелизм данных (библиотека параллельных задач)

Лямбда-выражения в PLINQ и библиотеке параллельных задач