Compartilhar via


Como gravar um loop Parallel.ForEach com variáveis locais de partição

O exemplo a seguir mostra como gravar um método ForEach que usa variáveis locais de partição. Quando um loop ForEach é executado, ele divide a coleção de origem em diversas partições. Cada partição tem sua própria cópia da variável local de partição. Uma variável local de partição é semelhante a uma variável local de thread, com a diferença de que várias partições podem ser executadas em um único thread.

O código e os parâmetros desse exemplo parecem com o método For correspondente. Para saber mais, confira Como escrever um loop Parallel.For com variáveis locais de thread.

Para usar uma variável local de partição em um loop ForEach, você deve chamar uma das sobrecargas de método que usa dois parâmetros de tipo. O primeiro parâmetro de tipo, TSource, especifica o tipo do elemento de origem. Já o segundo, TLocal, especifica o tipo da variável local de partição.

Exemplo

O exemplo a seguir chama a sobrecarga Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) para calcular a soma de uma matriz com um milhão de elementos. Essa sobrecarga tem quatro parâmetros:

  • source, que é a fonte de dados. Ela deve implementar IEnumerable<T>. A fonte de dados do nosso exemplo é um objeto IEnumerable<Int32> membro de um milhão retornado pelo método Enumerable.Range.

  • localInit, ou a função que inicia a variável local de partição. Essa função é chamada uma vez para cada partição onde a operação Parallel.ForEach é executada. Nosso exemplo inicia a variável local de partição como zero.

  • body, um Func<T1,T2,T3,TResult> que é invocado pelo loop paralelo em cada iteração do loop. Sua assinatura é Func\<TSource, ParallelLoopState, TLocal, TLocal>. Você fornece o código para o representante e o loop passa pelos parâmetros de entrada, que são:

    • O elemento atual de IEnumerable<T>.

    • Uma variável ParallelLoopState que pode ser usada no código do representante para avaliar o estado do loop.

    • A variável local de partição.

    Seu delegado retorna a variável local de partição, que é passada para a próxima iteração do loop que executa nessa partição específica. Cada partição do loop mantém uma instância separada dessa variável.

    Nesse exemplo, o delegado adiciona o valor de cada inteiro à variável local de partição, que mantém um total de valores dos elementos inteiros dessa partição em execução.

  • localFinally, um representante Action<TLocal> que o Parallel.ForEach invoca quando são concluídas as operações de loop de cada partição. O método Parallel.ForEach passa o valor final da variável local de partição dessa partição de loop para seu delegado Action<TLocal>, e você fornece o código que executa a ação necessária para combinar o resultado dessa partição aos resultados de outras partições. Esse representante pode ser invocado ao mesmo tempo por diversas tarefas. Por isso, o exemplo usa o método Interlocked.Add(Int32, Int32) para sincronizar o acesso à variável total. Como o tipo de representante é um Action<T>, não há valor retornado.

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

Confira também