Dela via


Gör så här: Skriva en parallell.For-loop med trådlokala variabler

Det här exemplet visar hur du använder trådlokala variabler för att lagra och hämta tillstånd i varje separat uppgift som skapas av en For loop. Genom att använda trådlokala data kan du undvika att synkronisera ett stort antal åtkomster till delat tillstånd. I stället för att skriva till en delad resurs för varje iteration beräknar och lagrar du värdet tills alla iterationer för aktiviteten har slutförts. Du kan sedan skriva slutresultatet en gång till den delade resursen eller skicka det till en annan metod.

Exempel

I följande exempel anropas For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) metoden för att beräkna summan av värdena i en matris som innehåller en miljon element. Värdet för varje element är lika med dess 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 två första parametrarna för varje For metod anger iterationsvärdena för början och slut. I den här överlagringen av metoden är den tredje parametern där du initierar ditt lokala tillstånd. I det här sammanhanget innebär lokalt tillstånd en variabel vars livslängd sträcker sig från strax före den första iterationen av loopen i den aktuella tråden till strax efter den senaste iterationen.

Typen av den tredje parametern är en Func<TResult> där TResult är den typ av variabel som lagrar trådlokalt tillstånd. Dess typ definieras av det generiska typargument som anges när den generiska For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) metoden anropas, vilket i det här fallet är Int64. Typargumentet anger för kompilatorn vilken typ av tillfällig variabel som ska användas för att lagra trådlokalt tillstånd. I det här exemplet initierar uttrycket () => 0 (eller Function() 0 i Visual Basic) den trådlokala variabeln till noll. Om det allmänna typargumentet är en referenstyp eller användardefinierad värdetyp skulle uttrycket se ut så här:

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

Den fjärde parametern definierar looplogik. Det måste vara ett ombuds- eller lambda-uttryck vars signatur finns Func<int, ParallelLoopState, long, long> i C# eller Func(Of Integer, ParallelLoopState, Long, Long) i Visual Basic. Den första parametern är värdet för loopräknaren för den iterationen av loopen. Det andra är ett ParallelLoopState objekt som kan användas för att bryta sig ut ur loopen. Det här objektet tillhandahålls av Parallel klassen till varje förekomst av loopen. Den tredje parametern är den trådlokala variabeln. Den sista parametern är returtypen. I det här fallet beror Int64 typen på att det är den typ som vi angav i typargumentet For . Variabeln heter subtotal och returneras av lambda-uttrycket. Returvärdet används för att initiera subtotal varje efterföljande iteration av loopen. Du kan också se den här sista parametern som ett värde som skickas till varje iteration och sedan skickas till ombudet localFinally när den senaste iterationen är klar.

Den femte parametern definierar den metod som anropas en gång, när alla iterationer i en viss tråd har slutförts. Typen av indataargument motsvarar återigen metodens typargument For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) och den typ som returneras av uttrycket lambda i brödtexten. I det här exemplet läggs värdet till i en variabel i klassomfånget på ett trådsäkert sätt genom att anropa Interlocked.Add metoden. Genom att använda en trådlokal variabel har vi undvikit att skriva till den här klassvariabeln för varje iteration av loopen.

Mer information om hur du använder lambda-uttryck finns i Lambda-uttryck i PLINQ och TPL.

Se även