begin process at 2012 02 11 10:51:20
  Trouver un code source :
 
dans
 
Accueil > 

Tutoriels

 > 

.NET

 > Programmation parallèle et multi-c½ur avec .NET

Programmation parallèle et multi-c½ur avec .NET


 Information sur le tutoriel

Note :
Aucune note

 Description

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.

Tutorial

Optimisation du code géré pour les machines multi-cœur, ‘Parallel FX’.


La TPL

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 '.


Les méthodes proposées

- Itérations indépendantes les unes des autres

Parallel.For(<début>, <fin>, delegate(<type> i) {<..actions..>});


- Itérations parallèles en concurrence pour le même verrou (pour C#4)

Ex: une somme conditionnelle sur une variable

    var i = Parallel.Aggregate(<début>, <fin>, <valeur de départ>, <..action sur chacun des éléments..>, <..action au final..>);

        Ex pour '<..sur chacun des éléments..>' : delegate(int x) { return x*10; }

        Ex pour '<..action au final..>' : delegate(int x, int y) { return x+y; }


- Lancer des tâches en parallèle

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.


- Tâches parallèles générales

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.

- Tâches qui calculent et retournent un résultat

Un Future est une tâche qui calcule un résultat. Ce résultat est un délégué avec le type Func<T> 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;


- Les exceptions

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.


- Gestionnaire des tâches

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.


Traitement des données

- Données, de LINQ à PLINQ

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.


- Collections

Dans System.Collections.Concurrent

    - BlockingCollection<T> (IProducerConsumerCollection<T>)

    - ConcurrentBag<T>

    - ConcurrentDictionary<TKey, TValue>

    - ConcurrentQueue<T> (anciennement ConcurrentLinkedList<T>)

    - ConcurrentStack<T>


- Synchronisation

Dans System.Threading

    - Barrier qui permet à plusieurs threads de travailler sur un algorithme en parallèle, puis pour chacun, de signaler son arrivée et ensuite de bloquer jusqu'à ce que certaines ou toutes les tâches soient arrivées.

    - CountdownEvent

    - ManualResetEventSlim comme ManualResetEvent

    - SemaphoreSlim, limite le nombre de thread pour avoir un acces concurrent à la ressource

    - SpinLock comme lock

    - SpinWait

    - WriteOnce<T>

    - LazyInit<T>


- Permettre que la mémoire d'un objet ne soit pas attribuée tant que ce n'est pas nécessaire : c'est le Lazy

    - System.Lazy<T> (anciennement System.LazyVariable<T>)

    - System.Threading.ThreadLocal<T>

    - System.Threading.LazyInitializer


Conclusion

- Discussion sur l’évolution et l’efficacité de la TPL

Les réticences envers la programmation asynchrone (et à plus forte raison avec le multi cœur) 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 cœur.

Les gains de performance annoncés avec la multiplication des machines équipées des plusieurs cœurs 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 !!


Commentaires

Commentaire de TheManu le 29/05/2009 15:28:56

Notes :
- La TPL est aussi utilisable sur une machine mono-c½ur où elle gérera les tâches (threads) de façon classique.
- La TPL est destinée a utiliser de façon optimale tous les c½urs. Pour cela .Net 4 sera amené a gérer une file d'attente de threads globale et une par c½ur.
Ce système dispatchera ses threads de façon équitable. Ainsi chaque thread (et surtout son contexte) se retrouvera en priorité sur un c½ur particulier. Et il y aura aussi une gestion en temps réel : le vol sera possible sur une file particulière dont le c½ur est occupé pour un au repos.
Ainsi on est sûr de remplir au mieux le processeur et ce en permanence.

Commentaire de biga1 le 24/06/2009 20:28:14

salut, j'ai apprecie le ptit tuto que t'as concu mais j'aimerai en savoir plus sur ce domaine.serait-il possible de nous envoyer d'autres tutos

Commentaire de TheManu le 25/06/2009 17:03:49

Salut Biga1,
Le sujet est pas mal discuté notamment sur des forums (la plupart du temps en anglais) ou dans des magazines ('Programmez', etc)..
La bible reste le site MSDN : http://msdn.microsoft.com/en-us/library/dd460693(VS.100).aspx
Il y a fort à parier que le multi-c½ur soit très 'présent' dans le futur .Net v4. Les choses seront alors mieux cadrées...

Commentaire de deadhand le 02/04/2010 11:33:09

Super utile !!! Merci

Commentaire de GG29 le 08/04/2010 23:19:34

en effet Vivement le C#4 !

Commentaire de TheManu le 11/05/2010 16:36:06

aujourd'hui : http://www.microsoft.com/france/vision/mstechdays10/Webcast.aspx?EID=0536845e-c963-4154-b22a-c357b7534ae3

 Ajouter un commentaire




Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

Photothèque

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,140 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales