Delen via


Procedure: Een parallelle.for-lus schrijven met thread-lokale variabelen

In dit voorbeeld ziet u hoe u thread-lokale variabelen gebruikt om de status op te slaan en op te halen in elke afzonderlijke taak die door een For lus wordt gemaakt. Door thread-lokale gegevens te gebruiken, kunt u de overhead van het synchroniseren van een groot aantal toegang tot de gedeelde status voorkomen. In plaats van bij elke iteratie naar een gedeelde resource te schrijven, berekent en slaat u de waarde op totdat alle iteraties voor de taak zijn voltooid. Vervolgens kunt u het uiteindelijke resultaat eenmaal naar de gedeelde resource schrijven of doorgeven aan een andere methode.

Opmerking

In het volgende voorbeeld wordt de For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) methode aangeroepen om de som van de waarden in een matrix te berekenen die één miljoen elementen bevat. De waarde van elk element is gelijk aan de index.

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Test
{
    static void Main()
    {
        int[] nums = Enumerable.Range(0, 1_000_000).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;
            },
            subtotal => Interlocked.Add(ref total, subtotal));

        Console.WriteLine("The total is {0:N0}", total);
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}
'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(subtotal) Interlocked.Add(total, subtotal))

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

End Module

De eerste twee parameters van elke For methode geven de begin- en einditeratiewaarden op. Bij deze overbelasting van de methode is de derde parameter waar u de lokale status initialiseert. In deze context betekent de lokale status een variabele waarvan de levensduur net vóór de eerste iteratie van de lus op de huidige thread loopt tot vlak na de laatste iteratie.

Het type van de derde parameter is een Func<TResult> locatie waar TResult het type variabele is waarmee de lokale threadstatus wordt opgeslagen. Het type wordt gedefinieerd door het algemene typeargument dat wordt opgegeven bij het aanroepen van de algemene For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) methode, in dit geval Int64. Het typeargument vertelt de compiler het type van de tijdelijke variabele die wordt gebruikt voor het opslaan van de lokale threadstatus. In dit voorbeeld initialiseert de expressie () => 0 (of Function() 0 in Visual Basic) de thread-lokale variabele naar nul. Als het algemene typeargument een verwijzingstype of door de gebruiker gedefinieerd waardetype is, ziet de expressie er als volgt uit:

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

De vierde parameter definieert de luslogica. Het moet een gemachtigde of lambda-expressie zijn waarvan de handtekening zich Func<int, ParallelLoopState, long, long> in C# of Func(Of Integer, ParallelLoopState, Long, Long) in Visual Basic bevindt. De eerste parameter is de waarde van de lusteller voor die herhaling van de lus. De tweede is een ParallelLoopState object dat kan worden gebruikt om de lus uit te breken. Dit object wordt door de Parallel klasse geleverd aan elk exemplaar van de lus. De derde parameter is de thread-locale variabele. De laatste parameter is het retourtype. In dit geval is Int64 het type omdat dat het type is dat we hebben opgegeven in het For typeargument. Deze variabele heeft de naam subtotal en wordt geretourneerd door de lambda-expressie. De retourwaarde wordt gebruikt om elke volgende iteratie van de lus te initialiseren subtotal . U kunt deze laatste parameter ook beschouwen als een waarde die wordt doorgegeven aan elke iteratie en vervolgens doorgegeven aan de localFinally gemachtigde wanneer de laatste iteratie is voltooid.

De vijfde parameter definieert de methode die eenmaal wordt aangeroepen, nadat alle iteraties op een bepaalde thread zijn voltooid. Het type van het invoerargument komt opnieuw overeen met het typeargument van de For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) methode en het type dat wordt geretourneerd door de lambda-expressie van de hoofdtekst. In dit voorbeeld wordt de waarde toegevoegd aan een variabele in het klassebereik op een veilige manier door de methode aan te Interlocked.Add roepen. Door een thread-lokale variabele te gebruiken, hebben we voorkomen dat er bij elke iteratie van de lus naar deze klassevariabele wordt geschreven.

Zie Lambda-expressies in PLINQ en TPL voor meer informatie over het gebruik van lambda-expressies.

Zie ook