C'est juste une petite présentation de la TPL (Task Parallel Library).
Note : j'ai mis très peu d'exemples puisque les classes de la TPL me semble suffisamment parlantes... Si vous avez toutefois des questions n'hésitez pas sans toutefois oublier que la TPL n'est pas figée.
La TPL (Task Parallel Library) est conçue pour faciliter l'écriture de code géré capable d'utiliser plusieurs processeurs automatiquement. À l'aide de cette bibliothèque, vous pouvez facilement exprimer le parallélisme potentiel d'un code séquentiel existant, où les tâches parallèles exposées seront exécutées simultanément sur tous les processeurs disponibles.
Information MSDN : http://msdn.microsoft.com/en-us/library/dd460693(VS.100).aspx
Espace de nom : System.Threading.
Il faut installer 'Microsoft Parallel Extensions to .NET Framework 3.5 CTP'. Puis faire référence dans son projet à la dll du dossier d'installation : 'C:\Program Files\Microsoft Parallel Extensions ... CTP\System.Threading.dll '.
Parallel.For(<début>, <fin>, delegate(<type> i) {<..actions..>});
Ex:
Parallel.Invoke(<..action1..>, <..action2..>, ...); (anciennement Parallel.Do(<..action1..>, <..action2..>, ...);)
Ou sous forme d'un tableau de tâches : Task[] tasks = { Task.Factory.StartNew(<..action1..>), .. }; qui pourrait être suivi de Task.WaitAll(tasks); pour une attente de fin de traitement.
Elles s'appuient sur la class Task. On peut la comparer à Thread mais en fait elle permet d'avantage.
class Task { Task( Action action ); void Wait(); void Cancel(); bool IsCompleted { get; } ... }
Note : toutes les méthodes statiques de la classe Parallel utilisent l'objet Task.
Un `Future' est une tâche qui calcule un résultat. Ce résultat est un délégué avec le type Func<T> où T est le type de valeur du futur.
Future<type> action = Future.Create(<..action..>); puis action.Value;
Le résultat du futur est récupéré par la propriété Value. La propriété Value appelle Wait en interne pour s'assurer que la tâche est achevée et que la valeur de résultat a été calculée.
De même : var action = Task<type>.Factory.StartNew(<..action..>); puis pour avoir le résultat : action.Result;
Toute exception levée dans l'action associée est stockée dans une tâche et levée à nouveau lorsque Wait est appelé. De même, les fonctions Parallel.For et Parallel.Do accumulent toutes les exceptions déclenchées et les lèvent à nouveau lorsque toutes les tâches sont terminées. Ceci garantit que les exceptions ne sont jamais perdues et sont correctement propagées aux dépendants.
Le Pool de threads est géré automatiquement par la TPL : Toutes les tâches appartiennent à un gestionnaire des tâches, qui, comme son nom l'indique, gère les tâches et veille à ce que les threads de travail exécutent ces tâches. Si un gestionnaire des tâches est toujours disponible par défaut, une application peut également en créer un de manière explicite.
Outre l'écriture des requêtes LINQ de la même façon que vous le feriez LINQ, il y a deux étapes supplémentaires nécessaires à l'utilisation de PLINQ :
1. Référencer l'assembly System.Concurrency.dll pendant la compilation.
2. Encapsuler votre source de données dans un élément IParallelEnumerable<T> avec un appel de la méthode d'extension System.Linq.ParallelEnumerable.AsParallel.
L'appel de la méthode d'extension AsParallel à l'étape 2 s'assure que le compilateur C# ou Visual Basic est relié à la version System.Linq.ParallelEnumerable des opérateurs de requête standard au lieu de System.Linq.Enumerable. Cela permet à PLINQ de prendre le contrôle et d'exécuter la requête en parallèle. AsParallel est défini comme utilisant n'importe quel IEnumerable<T>.
Ex :
var a = from élmt in list // avec list un List<int> where élmt < 8 select élmt;
devient :
var a = from élmt in list.AsParallel() where élmt < 8 select élmt;
Attention, après une opération (ou sélection élaborée) sur les éléments la conservation de l'ordre n'est pas garantie.
Dans System.Collections.Concurrent
Dans System.Threading
Les réticences envers la programmation asynchrone (et à plus forte raison avec le multi coeur) sont nombreuses pour beaucoup de programmeurs : difficulté de conception, de maintenance, de maitrise sur le débogage ou l'exécution... Ainsi qui n'a jamais oublié de protéger les accès à un objet `perdu' dans une méthode ?
Pourtant nous connaissons aussi les bienfaits qui en découlent : gains potentiels de performance, utilisation d'un système graphique, etc.
La TPL est appelé à évoluer et les classes -leur nom, leur nombre, leurs méthodes, ...- ne sont pas figées. C'est pourquoi elle est marqué CTP (Community Technology Preview, une version "beta" en quelque sorte). Malgré ce caractère non finalisé -assumé par Microsoft- elle promet une simplification de la programmation (multithread et) multi coeur.
Les gains de performance annoncés avec la multiplication des machines équipées des plusieurs coeurs est sans équivoque. Cependant doit on basculer dans le tout parallèle au niveau du code ? Non et ce pour plusieurs raisons :
Paralléliser son code reste très difficile même si la syntaxe de la TPL simplifie les choses. Or ce temps d'adaptation peut devenir inacceptable.
Généralement, seule une petite partie critique du programme réclame une optimisation en terme de vitesse.
L'initialisation de la TPL prend un certain temps et rend son utilisation inappropriée pour les touts petits traitements.
Il faut donc raison garder mais on peut saluer cette évolution (future) de .Net... Vivement C#4 !!