Partager via


Guide pratique pour écrire une boucle Parallel.ForEach avec des variables locales de partition

L’exemple suivant montre comment écrire une méthode ForEach qui utilise des variables locales de partition. Quand une boucle ForEach s’exécute, elle divise sa collection source en plusieurs partitions. Chaque partition a sa propre copie de la variable locale de partition. Une variable locale de partition est similaire à une variable locale de thread, à la différence près que plusieurs partitions peuvent s’exécuter sur un thread unique.

Le code et les paramètres dans cet exemple ressemblent beaucoup à la méthode For correspondante. Pour plus d’informations, voir Guide pratique : écrire une boucle Parallel.For avec des variables locales de thread.

Pour utiliser une variable locale de partition dans une boucle ForEach, vous devez appeler l’une des surcharges de méthode qui accepte deux paramètres de type. Le premier paramètre de type, TSource, spécifie le type d’élément source, tandis que le second, TLocal, spécifie le type de la variable locale de partition.

Exemple

L’exemple suivant appelle la surcharge Parallel.ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) pour calculer la somme d’un tableau d’un million d’éléments. Cette surcharge possède quatre paramètres :

  • source, qui représente la source de données. Il doit implémenter IEnumerable<T>. La source de données dans notre exemple est l'objet IEnumerable<Int32> d'un million de membres retourné par la méthode Enumerable.Range.

  • localInit ou fonction qui initialise la variable locale de partition. Cette fonction est appelée une fois pour chaque partition dans laquelle l'opération Parallel.ForEach s'exécute. Notre exemple initialise la variable locale de partition à zéro.

  • body, Func<T1,T2,T3,TResult> appelé par la boucle parallèle sur chaque itération de la boucle. Sa signature est Func\<TSource, ParallelLoopState, TLocal, TLocal>. Vous fournissez le code pour le délégué, puis la boucle transmet les paramètres d'entrée, qui sont :

    • l'élément actuel de l'interface IEnumerable<T> ;

    • une variable ParallelLoopState que vous pouvez utiliser dans le code de votre délégué pour examiner l'état de la boucle ;

    • la variable locale de partition.

    Votre délégué retourne la variable locale de partition, qui est ensuite transmise à la prochaine itération de la boucle qui s’exécute dans la partition concernée. Chaque partition de boucle tient à jour une instance distincte de cette variable.

    Dans l’exemple, le délégué ajoute la valeur de chaque entier à la variable locale de partition, qui tient à jour le total cumulé des valeurs des éléments entiers dans la partition concernée.

  • localFinally, délégué Action<TLocal> que la méthode Parallel.ForEach appelle quand les opérations de bouclage dans chaque partition sont terminées. La méthode Parallel.ForEach passe à votre délégué Action<TLocal> la valeur finale de la variable locale de partition pour cette partition de boucle, et vous fournissez le code qui prend en charge la combinaison du résultat de cette partition avec les résultats des autres partitions. Ce délégué peut être appelé simultanément par plusieurs tâches. C’est la raison pour laquelle l’exemple utilise la méthode Interlocked.Add(Int32, Int32) pour synchroniser l’accès à la variable total. Comme le type de délégué est un délégué Action<T>, aucune valeur n'est retournée.

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

Voir aussi