Comment : écrire une boucle Parallel.For comprenant des variables locales de thread
Cet exemple indique comment utiliser des variables locales de thread pour stocker et récupérer l'état de chaque tâche créée par une boucle For. En utilisant des données locales de thread, vous pouvez éviter la surcharge de synchronisation d'un grand nombre d'accès à un état partagé. Au lieu d'écrire dans une ressource partagée sur chaque itération, vous calculez et stockez la valeur jusqu'à ce que toutes les itérations de la tâche soient terminées. Vous pouvez ensuite écrire une fois le résultat final dans la ressource partagée ou le passer à une autre méthode.
Exemple
'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();
}
}
}
Les deux premiers paramètres de chaque méthode For spécifient le début et la fin des valeurs d'itération. Dans cette surcharge de méthode, le troisième paramètre se trouve là où vous initialisez votre état local. " Dans ce contexte, « état local » signifie une variable dont la durée de vie commence juste avant la première itération de la boucle sur le thread actuel et se termine juste après la dernière itération.
Le type du troisième paramètre est Func<TResult>, TResult étant le type de la variable qui stockera l'état local de thread. Notez que dans cet exemple, une version générique de la méthode est utilisée et que le paramètre de type est long (Long en Visual Basic). Le paramètre de type indique au compilateur le type de la variable temporaire qui sera utilisée pour stocker l'état local de thread. L'expression () => 0 (Function() 0 en Visual Basic) dans cet exemple signifie que la variable locale de thread est initialisée à zéro. Si le paramètre de type est un type référence ou un type valeur défini par l'utilisateur, ce Func ressemblerait à ceci :
() => new MyClass()
Function() new MyClass()
Le quatrième paramètre de type se trouve là où vous définissez la logique de boucle. IntelliSense affiche que son type est Func<int, ParallelLoopState, long, long> ou Func(Of Integer, ParallelLoopState, Long, Long). L'expression lambda attend trois paramètres d'entrée dans ce même ordre correspondant à ces types. Le dernier paramètre de type est un type de retour. Dans ce cas, le type est long parce qu'il s'agit du type que nous avons spécifié dans le paramètre de type For. Nous appelons cette variable subtotal dans l'expression lambda et la retournons. La valeur de retour est utilisée pour initialiser le sous-total sur chaque itération suivante. Vous pouvez également considérer ce dernier paramètre comme une valeur passée à chaque itération, puis au délégué localFinally lorsque la dernière itération est terminée.
Le cinquième paramètre se trouve là où vous définissez la méthode qui sera appelée une fois, après que se soient terminées toutes les itérations sur ce thread. Le type du paramètre d'entrée correspond de nouveau au paramètre de type de la méthode For et au type retourné par l'expression lambda du corps. Dans cet exemple, la valeur est ajoutée à une variable à portée de classe d'une façon thread-safe. En utilisant une variable locale de thread, nous avons évité d'écrire dans cette variable de catégorie sur chaque itération de thread.
Pour plus d'informations sur l'utilisation des expressions lambda, consultez Expressions lambda en PLINQ et dans la bibliothèque parallèle de tâches.
Voir aussi
Concepts
Parallélisme de données (bibliothèque parallèle de tâches)
Programmation parallèle dans le .NET Framework
Bibliothèque parallèle de tâches
Expressions lambda en PLINQ et dans la bibliothèque parallèle de tâches