Udostępnij za pośrednictwem


Instrukcje: zapisywanie pętli Parallel.ForEach ze zmiennymi lokalnymi partycji

W poniższym przykładzie pokazano, jak napisać metodę używającą ForEach zmiennych lokalnych partycji. Gdy pętla ForEach się wykonuje, dzieli kolekcję źródłową na wiele partycji. Każda partycja ma własną kopię zmiennej partycji lokalnej. Zmienna lokalna partycji jest podobna do zmiennej wątkowej lokalnej, z tą różnicą, że wiele partycji może być uruchamianych w jednym wątku.

Kod i parametry w tym przykładzie są ściśle podobne do odpowiedniej For metody. Aby uzyskać więcej informacji, zobacz How to: Write a Parallel.For Loop with Thread-Local Variables (Instrukcje: zapisywanie pętli Parallel.For za pomocą zmiennych Thread-Local).

Aby użyć zmiennej partycji lokalnej w ForEach pętli, należy wywołać jedno z przeciążeń metody, które przyjmuje dwa parametry typu. Pierwszy parametr typu , TSourceokreśla typ elementu źródłowego, a drugi parametr TLocaltypu , określa typ zmiennej partycji lokalnej.

Przykład

Poniższy przykład wywołuje Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) przeciążenie, aby obliczyć sumę tablicy z milionem elementów. To przeciążenie ma cztery parametry:

  • source, czyli źródło danych. Musi zaimplementować IEnumerable<T>. ** Źródłem danych w naszym przykładzie jest obiekt z milionem członków IEnumerable<Int32> zwrócony przez metodę Enumerable.Range.

  • localInit, lub funkcja, która inicjuje zmienną lokalną partycji. Ta funkcja jest wywoływana raz dla każdej partycji, w której jest wykonywana Parallel.ForEach operacja. W naszym przykładzie inicjuje zmienną lokalną partycji na zero.

  • body, który Func<T1,T2,T3,TResult> jest wywoływany przez pętlę równoległą w każdej iteracji pętli. Jego podpis to Func\<TSource, ParallelLoopState, TLocal, TLocal>. Należy podać kod dla delegata, a pętla przekazuje parametry wejściowe, które są następujące:

    • Bieżący element IEnumerable<T>.

    • Zmienna ParallelLoopState , której można użyć w kodzie delegata do zbadania stanu pętli.

    • Zmienna lokalna partycji.

    Delegat zwraca zmienną lokalną partycji, która jest następnie przekazywana do następnej iteracji pętli wykonywanej w tej konkretnej partycji. Każda partycja pętli utrzymuje oddzielne wystąpienie tej zmiennej.

    W tym przykładzie delegat dodaje wartość każdej liczby całkowitej do zmiennej lokalnej partition-local, która utrzymuje sumę bieżącą wartości elementów całkowitych w tej partycji.

  • localFinally, Action<TLocal> delegat, który jest wywoływany przez Parallel.ForEach po zakończeniu operacji pętli w każdej z partycji. Metoda Parallel.ForEach przekazuje delegatowi Action<TLocal> końcową wartość zmiennej lokalnej dla tej części pętli, a Ty dostarczasz kod, który wykonuje wymaganą akcję łączenia wyniku z tej części z wynikami z innych części. Ten delegat może być wywoływany współbieżnie przez wiele zadań. W związku z tym w przykładzie użyto Interlocked.Add(Int32, Int32) metody w celu zsynchronizowania dostępu do zmiennej total . Ponieważ typ delegata to Action<T>, nie ma wartości zwracanej.

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 {total:N0}");
    }
}
// The example displays the following output:
//        The total from Parallel.ForEach is 499,999,500,000
' How to: Write a Parallel.ForEach Loop That Has Thread-Local Variables

Imports System.Threading
Imports System.Threading.Tasks

Module ForEachThreadLocal
    Sub Main()

        Dim nums() As Integer = Enumerable.Range(0, 1000000).ToArray()
        Dim total As Long = 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(Of Integer, Long)(nums, Function() 0,
                                           Function(elem, loopState, subtotal)
                                               subtotal += elem
                                               Return subtotal
                                           End Function,
                                            Sub(finalResult)
                                                Interlocked.Add(total, finalResult)
                                            End Sub)

        Console.WriteLine("The result of Parallel.ForEach is {0:N0}", total)
    End Sub
End Module
' The example displays the following output:
'       The result of Parallel.ForEach is 499,999,500,000

Zobacz też