Condividi tramite


Procedura: scrivere un ciclo Parallel.For con variabili locali dei thread

In questo esempio viene mostrato come utilizzare variabili locali dei thread per archiviare e recuperare lo stato in ogni singola attività creata da un ciclo For. Tramite l'utilizzo di dati locali dei thread è possibile evitare l'overhead dovuto alla sincronizzazione di un numero elevato di accessi a uno stato condiviso. Anziché scrivere in una risorsa condivisa a ogni iterazione, si calcola e archivia il valore fino al completamento di tutte le iterazioni dell'attività. È quindi possibile scrivere un'unica volta il risultato finale nella risorsa condivisa o passarlo a un altro metodo.

Esempio

'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();
        }
    }
}

I primi due parametri di ogni metodo For specificano i valori delle iterazioni iniziale e finale. Il terzo parametro di questo overload del metodo viene utilizzato per inizializzare lo stato locale. " Il termine "stato locale" in questo contesto indica una variabile la cui durata si estende da poco prima della prima iterazione del ciclo nel thread corrente a subito dopo l'ultima iterazione.

Il tipo del terzo parametro è un oggetto Func<TResult>, mentre TResult è il tipo della variabile in cui verrà archiviato lo stato locale del thread. Si noti che in questo esempio viene utilizzata una versione generica del metodo e il parametro di tipo è long (Long in Visual Basic). Il parametro di tipo indica al compilatore il tipo della variabile temporanea che verrà utilizzata per archiviare lo stato locale del thread. L'espressione () => 0 (Function() 0 in Visual Basic) in questo esempio significa che la variabile locale del thread viene inizializzata su zero. Se il parametro di tipo è un tipo di riferimento o un tipo di valore definito dall'utente, questo Func sarà analogo al seguente:

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

Il quarto parametro di tipo viene utilizzato per definire la logica di ciclo. Il tipo visualizzato da IntelliSense è Func<int, ParallelLoopState, long, long> o Func(Of Integer, ParallelLoopState, Long, Long). L'espressione lambda prevede tre parametri di input nello stesso ordine corrispondente a tali tipi. L'ultimo parametro di tipo è il tipo restituito. In questo caso il tipo è long poiché nel parametro di tipo For è stato specificato questo tipo. Tale variabile, dopo essere stata denominata subtotal nell'espressione lambda, viene quindi restituita. Il valore restituito viene utilizzato per inizializzare subtotal in ogni iterazione successiva. Quest'ultimo parametro può anche essere visto semplicemente come un valore passato a ogni iterazione e quindi al delegato localFinally al completamento dell'ultima iterazione.

Il quinto parametro viene utilizzato per definire il metodo che verrà chiamato una sola volta dopo il completamento di tutte le iterazioni in questo thread. Anche in questo caso, il tipo del parametro di input corrisponde al parametro di tipo del metodo For e al tipo restituito dall'espressione lambda del corpo. In questo esempio il valore viene aggiunto a una variabile nell'ambito della classe in modo thread-safe. Tramite una variabile di thread locale non è stato necessario scrivere in questa variabile della classe a ogni iterazione di ogni thread.

Per ulteriori informazioni sull'utilizzo delle espressioni lambda, vedere Espressioni lambda in PLINQ e TPL.

Vedere anche

Concetti

Parallelismo dei dati (Task Parallel Library)

Programmazione parallela in .NET Framework

Task Parallel Library

Espressioni lambda in PLINQ e TPL