Freigeben über


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

In diesem Beispiel wird gezeigt, wie Sie mithilfe von lokalen Threadvariablen den Zustand in jeder separaten Aufgabe speichern und abrufen, die von einer For-Schleife erstellt wird. Durch die Verwendung lokaler Threaddaten können Sie den Mehraufwand vermeiden, der mit der Synchronisierung einer großen Anzahl von Zugriffen auf den Freigabezustand verbunden ist. Anstatt für jede Iteration in eine freigegebenen Ressource zu schreiben, berechnen Sie den Wert und speichern ihn so lange, bis alle Iterationen für die Aufgabe abgeschlossen sind. Anschließend können Sie das Endergebnis einmal in die freigegebene Ressource schreiben oder an eine andere Methode übergeben.

Beispiel

'How to: Write a Parallel.For Loop That Has Thread-Local Variables

Imports System.Threading
Imports System.Threading.Tasks

Module ForWithThreadLocal

    Sub Main()
        Dim nums As Integer() = Enumerable.Range(0, 1000000).ToArray()
        Dim total As Long = 0

        ' Use type parameter to make subtotal a Long type. Function will overflow otherwise.
        Parallel.For(Of Long)(0, nums.Length, Function() 0, Function(j, [loop], subtotal)
                                                                subtotal += nums(j)
                                                                Return subtotal
                                                            End Function, Function(x) Interlocked.Add(total, x))

        Console.WriteLine("The total is {0}", total)
        Console.WriteLine("Press any key to exit")
        Console.ReadKey()
    End Sub

End Module
namespace ThreadLocalFor
{
    using System;
    using System.Collections.Generic;
    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;

            // Use type parameter to make subtotal a long, not an int
            Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
            {
                subtotal += nums[j];
                return subtotal;
            },
                (x) => Interlocked.Add(ref total, x)
            );

            Console.WriteLine("The total is {0}", total);
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }
}

Die ersten zwei Parameter jeder For-Methode geben den Anfangs- und Enditerationswert an. In dieser Überladung der Methode wird im dritten Parameter der lokale Zustand initialisieren. " "Lokaler Zustand" in diesem Kontext bezieht sich auf eine Variable, deren Lebensdauer von kurz vor der ersten Iteration der Schleife im aktuellen Thread bis kurz nach der letzten Iteration reicht.

Der Typ des dritten Parameters ist ein Func<TResult>, wobei TResult den Typ der Variablen angibt, in der der lokale Threadzustand gespeichert wird. Beachten Sie, dass in diesem Beispiel eine generische Version der Methode verwendet wird und dass der Typparameter long (Long in Visual Basic) lautet. Der Typparameter teilt dem Compiler den Typ der temporären Variablen mit, die zum Speichern des lokalen Threadzustands verwendet wird. Der Ausdruck () => 0 (Function() 0 in Visual Basic) in diesem Beispiel bedeutet, dass die lokale Threadvariable mit 0 (null) initialisiert wird. Wenn der Typparameter ein Verweistyp oder benutzerdefinierter Werttyp ist, sieht die Funktion wie folgt aus:

() => new MyClass()
Function() new MyClass()

Im vierten Typparameter wird die Schleifenlogik definiert. IntelliSense zeigt als Typ Func<int, ParallelLoopState, long, long> oder Func(Of Integer, ParallelLoopState, Long, Long) an. Der Lambda-Ausdruck erwartet drei Eingabeparameter in der gleichen Reihenfolge wie diese Typen. Der letzte Typparameter ist der Rückgabetyp. In diesem Fall lautet der Typ "long", da dies der Typ ist, der im For-Typparameter angegeben wurde. Die Variable subtotal wird im Lambda-Ausdruck aufgerufen und zurückgegeben. Der Rückgabewert wird verwendet, um in allen nachfolgenden Iterationen eine Zwischensumme zu initialisieren. Sie können diesen letzten Parameter auch einfach als Wert betrachten, der an jede Iteration und nach Abschluss der letzten Iteration an denlocalFinally-Delegaten übergeben wird.

Im fünften Parameter definieren Sie die Methode, die ein Mal aufgerufen wird, nachdem alle Iterationen in diesem Thread abgeschlossen wurden. Der Typ des Eingabeparameters entspricht wiederum dem Typparameter der For-Methode und dem vom Lambda-Ausdruck im Text zurückgegebenen Typ. In diesem Beispiel wird der Wert zu einer Variablen im Klassengültigkeitsbereich auf eine threadsichere Weise hinzugefügt. Durch die Verwendung einer lokalen Threadvariablen haben wir vermieden, bei jeder Iteration des Threads in diese Klassenvariable schreiben zu müssen.

Weitere Informationen zur Verwendung von Lambda-Ausdrücken finden Sie unter Lambda-Ausdrücke in PLINQ und TPL.

Siehe auch

Konzepte

Datenparallelität (Task Parallel Library)

Parallele Programmierung in .NET Framework

Task Parallel Library

Lambda-Ausdrücke in PLINQ und TPL