Sdílet prostřednictvím


Jak napsat smyčku Parallel.For s Thread-Local proměnnými

Tento příklad ukazuje, jak pomocí místních proměnných vláken ukládat a načítat stav v jednotlivých samostatných úlohách vytvořených smyčkou For . Použitím vláknově lokálních dat můžete předejít režii synchronizace velkého počtu přístupů ke sdílenému stavu. Místo zápisu do sdíleného prostředku v každé iteraci vypočítáte a uložíte hodnotu, dokud nebudou dokončeny všechny iterace úkolu. Konečný výsledek pak můžete jednou napsat do sdíleného prostředku nebo ho předat jiné metodě.

Příklad

Následující příklad volá metodu For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) k výpočtu součtu hodnot v matici, která obsahuje jeden milion prvků. Hodnota každého prvku je rovna jeho indexu.

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 {total:N0}");
        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

První dva parametry každé For metody určují počáteční a koncové hodnoty iterace. Při tomto přetížení metody slouží třetí parametr k inicializaci místního stavu. V tomto kontextu znamená místní stav proměnnou, jejíž životnost začíná těsně před první iterací smyčky na aktuálním vlákně a končí těsně po poslední iteraci.

Typ třetího parametru je Func<TResult>TResult typ proměnné, která uloží místní stav vlákna. Jeho typ je definován obecným typovým argumentem zadaným při volání této obecné For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) metody, což je v tomto případě Int64. Argument typu říká kompilátoru typ dočasné proměnné, která bude použita k uložení stavu thread-local. V tomto příkladu výraz () => 0 (nebo Function() 0 v jazyce Visual Basic) inicializuje lokální proměnnou vlákna na nulu. Pokud je argument obecného typu odkaz nebo uživatelem definovaný typ hodnoty, výraz by vypadal takto:

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

Čtvrtý parametr definuje logiku smyčky. Musí to být výraz delegáta nebo lambda, jehož podpis je Func<int, ParallelLoopState, long, long> v jazyce C# nebo Func(Of Integer, ParallelLoopState, Long, Long) v jazyce Visual Basic. Prvním parametrem je hodnota čítače smyčky pro tuto iteraci smyčky. Druhá je ParallelLoopState objekt, který lze použít k přerušení smyčky; tento objekt je poskytován Parallel třídou pro každý výskyt smyčky. Třetí parametr je místní proměnná vlákna. Posledním parametrem je návratový typ. V tomto případě je typ Int64, protože je to typ, který jsme zadali v typu argumentu For. Tato proměnná je pojmenovaná subtotal a je vrácena výrazem lambda. Vrácená hodnota se používá k inicializaci subtotal pro každou následnou iteraci smyčky. Tento poslední parametr si také můžete představit jako hodnotu, která se předá každé iteraci, a pak se předá delegátovi localFinally po skončení poslední iterace.

Pátý parametr definuje metodu, která se volá jednou, po dokončení všech iterací v určitém vlákně. Typ vstupního argumentu znovu odpovídá argumentu For<TLocal>(Int32, Int32, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) typu metody a typu vráceného výrazem body lambda. V tomto příkladu se hodnota bezpečným způsobem přidá do proměnné v oboru třídy voláním metody Interlocked.Add. Pomocí proměnné s lokální platností v rámci vlákna jsme se vyhnuli zápisu do této proměnné na úrovni třídy při každé iteraci smyčky.

Další informace o použití výrazů lambda naleznete v tématu Výrazy lambda v PLINQ a TPL.

Viz také