Gewusst wie: Schreiben einer Parallel.ForEach-Schleife mit lokalen Threadvariablen

Im folgenden Beispiel wird gezeigt, wie eine ForEach-Methode geschrieben wird, für die threadlokale Variablen verwendet werden. Wenn eine ForEach-Schleife ausgeführt wird, wird die Quellauflistung in mehrere Partitionen unterteilt. Jede Partition erhält eine eigene Kopie der "threadlokalen" Variable. (Der Begriff "threadlokal" ist hier etwas ungenau, da in einigen Fälle zwei Partitionen möglicherweise im gleichen Thread ausgeführt werden.)

Der Code und die Parameter in diesem Beispiel ähneln stark der entsprechenden For-Methode. Weitere Informationen finden Sie unter Gewusst wie: Schreiben einer Parallel.For-Schleife mit lokalen Threadvariablen.

Beispiel

Um eine threadlokale Variable in einer ForEach-Schleife verwenden zu können, muss die Version der Methode verwendet werden, die zwei type-Parameter erhält. Der erste Parameter gibt den Typ des Quellelements und der zweite Parameter den Typ der threadlokalen Variable an.

Der erste Eingabeparameter ist die Datenquelle und der zweite die Funktion, die die threadlokale Variable initialisiert. Der dritte Eingabeparameter ist ein Func<T1, T2, T3, TResult>-Objekt, das von der parallelen Schleife in jeder Iteration aufgerufen wird. Sie stellen den Code für den Delegaten bereit, und die Schleife übergibt die Eingabeparameter. Die Eingabeparameter sind das aktuelle Element, eine ParallelLoopState-Variable, die die Prüfung des Zustands der Schleife ermöglicht, und die threadlokale Variable. Sie geben die threadlokale Variable zurück, und die Methode übergibt sie anschließend an die nächste Iteration auf dieser Partition. Diese Variable ist auf allen Schleifenpartitionen unterschiedlich.

Der letzte Eingabeparameter der ForEach-Methode ist der Action<T>-Delegat, den die Methode aufruft, wenn alle Schleifen abgeschlossen wurden. Von der Methode wird der endgültige Wert der threadlokalen Variable für diesen Thread (oder die Schleifenpartition) bereitgestellt, während der Benutzer den Code angibt, der den endgültigen Wert erfasst und jede Aktion ausführt, die für die Kombination des Ergebnisses aus dieser Partition mit den Ergebnissen aus den anderen Partitionen erforderlich ist. Da der Delegattyp ein Action<T>-Objekt ist, ist kein Rückgabewert vorhanden.

' 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();
        }
    }
}

Siehe auch

Aufgaben

Gewusst wie: Schreiben einer Parallel.For-Schleife mit lokalen Threadvariablen

Konzepte

Datenparallelität (Task Parallel Library)

Lambda-Ausdrücke in PLINQ und TPL