Markieren Sie das Kontrollkästchen Englisch, um die englische Version dieses Artikels anzuzeigen. Sie können den englischen Text auch in einem Popup-Fenster einblenden, indem Sie den Mauszeiger über den Text bewegen.
Übersetzung
Englisch

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

.NET Framework (current version)
 

Im folgenden Beispiel wird gezeigt, wie eine ForEach<TSource, TLocal>-Methode geschrieben wird, für die threadlokale Variablen verwendet werden. Wenn eine ForEach<TSource>-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 How to: Write a Parallel.For Loop with Thread-Local Variables.

Um eine threadlokale Variable in einer ForEach<TSource, TLocal>-Schleife verwenden zu können, müssen Sie eine der Methodenüberladungen aufrufen, die zwei type-Parameter erhält. Der erste type-Parameter, TSource, gibt den Typ des Quellelements, der zweite type-Parameter, TLocal, den Typ der threadlokalen Variable an.

Beispiel

Im folgenden Beispiel wird die Parallel.ForEach<TSource, TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource, ParallelLoopState, TLocal, TLocal>, Action<TLocal>)-Überladung aufgerufen, um die Summe eines Arrays mit einer Million Elementen zu berechnen. Diese Überladung hat vier Parameter:

  • source ist die Datenquelle. Der Parameter muss IEnumerable<T> implementieren. Die Datenquelle in unserem Beispiel ist IEnumerable<Int32>-Objekt mit einer Million Mitgliedern, das von der Enumerable.Range-Methode zurückgegeben wird.

  • localInit oder die Funktion, die die threadlokale Variable initialisiert. Diese Funktion wird einmal für jede Partition aufgerufen, in der der Parallel.ForEach<TSource>-Vorgang ausgeführt wird. In unserem Beispiel wird die threadlokale Variable auf Null initialisiert.

  • body, ein Func<T1, T2, T3, TResult>-Objekt, wird von der parallelen Schleife in jeder Iteration der Schleife aufgerufen. Die Signatur lautet Func<TSource, ParallelLoopState, TLocal, TLocal>. Sie stellen den Code für den Delegaten bereit, und die Schleife übergibt die folgenden Eingabeparameter:

    • Das aktuelle Element von IEnumerable<T>.

    • Eine ParallelLoopState-Variable, die Sie im Code Ihres Delegaten verwenden können, um den Zustand der Schleife zu untersuchen.

    • Die threadlokale Variable.

    Ihr Delegat gibt die threadlokale Variable zurück, die dann an die nächste Iteration der Schleife übergeben wird, die in dieser bestimmten Partition ausgeführt wird. Jede Schleifenpartition behält eine separate Instanz dieser Variable bei.

    In dem Beispiel fügt der Delegat den Wert jeder Ganzzahl zur threadlokalen Variable hinzu, die einen laufenden Gesamtbetrag der Werte der Ganzzahlelemente in dieser Partition beibehält.

  • localFinally, ein Action<TLocal>-Delegat, der von Parallel.ForEach<TSource> aufgerufen wird, wenn die Schleifenvorgänge in jeder Partition abgeschlossen wurden. Die Parallel.ForEach<TSource>-Methode übergibt Ihrem Action<TLocal>-Delegat den ersten Wert der threadlokalen Variable für diesen Thread (oder diese Schleifenpartition), und Sie stellen den Code bereit, der die erforderliche Aktion zum Kombinieren des Ergebnisses von dieser Partition mit den Ergebnissen der andere Partitionen bereit. Dieser Delegat kann von mehreren Aufgaben gleichzeitig aufgerufen werden. Darum verwendet das Beispiel die Interlocked.Add(Int32, Int32)-Methode, um den Zugriff auf die Variable total zu synchronisieren. Da der Delegattyp ein Action<T>-Objekt ist, ist kein Rückgabewert vorhanden.

 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
Anzeigen: