Condividi tramite


Procedura: Scrivere un ciclo Parallel.ForEach con variabili locali di partizione

Nell'esempio seguente viene illustrato come scrivere un ForEach metodo che usa variabili locali di partizione. Quando un ForEach ciclo viene eseguito, divide la raccolta di origine in più partizioni. Ogni partizione ha una propria copia della variabile partition-local. Una variabile partition-local è simile a una variabile locale del thread, ad eccezione del fatto che più partizioni possono essere eseguite in un singolo thread.

Il codice e i parametri in questo esempio sono simili al metodo corrispondente For . Per altre informazioni, vedere Procedura: Scrivere un ciclo Parallel.For con variabili Thread-Local.

Per usare una variabile locale di partizione in un ForEach ciclo, è necessario chiamare uno degli overload del metodo che accetta due parametri di tipo. Il primo parametro di tipo, TSource, specifica il tipo dell'elemento di origine e il secondo parametro di tipo, TLocal, specifica il tipo della variabile partition-local.

Esempio

Nell'esempio seguente viene chiamato l'overload Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) per calcolare la somma di una matrice di un milione di elementi. Questo overload ha quattro parametri:

  • source, ovvero l'origine dati. Deve implementare IEnumerable<T>. L'origine dati nel nostro esempio è l'oggetto con un milione di membri restituito dal metodo Enumerable.Range.

  • localInito la funzione che inizializza la variabile partition-local. Questa funzione viene chiamata una volta per ogni partizione in cui viene eseguita l'operazione Parallel.ForEach . Nell'esempio seguente la variabile partition-local viene inizializzata su zero.

  • body, oggetto Func<T1,T2,T3,TResult> richiamato dal ciclo parallelo in ogni iterazione del ciclo. La firma è Func\<TSource, ParallelLoopState, TLocal, TLocal>. Fornire il codice per il delegato e il ciclo passa i parametri di input, che sono:

    • Elemento corrente dell'oggetto IEnumerable<T>.

    • Variabile ParallelLoopState che è possibile usare nel codice del delegato per esaminare lo stato del ciclo.

    • Variabile locale alla partizione.

    Il delegato restituisce la variabile partition-local, che viene quindi passata all'iterazione successiva del ciclo eseguito in tale partizione specifica. Ogni partizione del ciclo gestisce un'istanza separata di questa variabile.

    Nell'esempio, il delegato aggiunge il valore di ogni intero alla variabile partition-local, che mantiene un totale in esecuzione dei valori degli elementi integer in tale partizione.

  • localFinally, un Action<TLocal> delegato richiamato Parallel.ForEach da quando le operazioni di ciclo in ogni partizione sono state completate. Il metodo Parallel.ForEach passa al tuo delegato Action<TLocal> il valore finale della variabile locale alla partizione per questa partizione del ciclo, e tu fornisci il codice che esegue l'azione necessaria per combinare il risultato di questa partizione con i risultati delle altre partizioni. Questo delegato può essere richiamato simultaneamente da più attività. Per questo motivo, l'esempio usa il Interlocked.Add(Int32, Int32) metodo per sincronizzare l'accesso alla total variabile. Poiché il tipo delegato è un oggetto Action<T>, non esiste alcun valore restituito.

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 {total:N0}");
    }
}
// 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

Vedere anche