Les Threads en .NET

Formation

En Ligne

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En ligne

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Les matières

  • .NET

Le programme

Introduction du cours

Il existe en .NET quelques créatures qui se cachent dans le noir. Parmi celles-ci sont les threads, les fonctions lambda et les delegates. N'ayez craintes chers Zéros, voici un tutoriel qui répondra à vos attentes en ce qui concerne les threads.

Par contre, l'étude de cette discipline requiert également une bonne connaissance du C#. Nous verrons également au passage les delegates, références sur des méthodes.

Je vous le dis maintenant, ce n'est pas un sujet des plus faciles, mais cela permet de nombreuses améliorations de vos applications, notamment pour leur permettre de communiquer en réseau, mais aussi de rendre fluide certaines opérations lourdes en traitement sans bloquer votre application !

Cours pour zéros avertis seulement ! Si vous ne connaissez pas le C#, ce tutoriel n'est pas pour vous.

Si vous désirez suivre le cours en VB.NET, je vous conseille d'utiliser un traducteur C# -> VB.NET afin de traduire les exemples. ;)

Les bases des delegatesLes delegates, une référence en la matière !Qu'est-ce qu'un delegate ?

Un delegate est un concept abstrait du C#. Jusqu'à maintenant, une variable pouvait contenir de nombreuses choses. On traite, par exemple, les objets comme des variables. Elles permettent aussi de mettre en mémoire des données, comme du texte, des nombres entiers ou flottants, des booléens. Ces cas ne sont que des exemples.

Un delegate est en fait une variable un peu spéciale... Elle ne sert qu'à donner une référence vers une méthode ou fonction. Elle est donc de type référence, comme un objet !

L'utilité sera bien évidemment d'envoyer ces delegates en paramètres ! Pensez-y, une méthode générique unique pourrait s'occuper de lancer un nombre infini d'opérations déterminées par vos bons soins. Ne vous en faites pas si ce concept est abstrait pour le moment. Comprenez seulement qu'on peut drastiquement augmenter la réutilisation du code avec cet outil. N'est-ce pas le grand but de la POO que de réutiliser le code ?

Comment on fait un delegate ? Ça paraît compliqué...

Oui, ça paraît compliqué aux premiers abords, mais ça devient vite facile. Allez, on s'y lance !

Partis pour la gloire !

Je vais commencer par vous donner un exemple bien simple, un cas que vous utilisez toujours lorsque vous programmez en C# !

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestThread { class Program { static void Main(string[] args) { bool resultat = Test("Ceci est un test qui est négatif !"); bool res2 = Test("Positif"); } static public bool Test(string test) { return test.Length < 15; } } }

Une fonction est un bloc de traitements qui retourne un résultat, et la méthode ne fait que des traitements sans rien retourner. Pour alléger le texte, d'ici la fin de ce tutoriel, j'appellerai toute fonction ou méthode des méthodes, indifféremment de leur traitement.

On voit clairement une situation très usuelle ici. Vous appelez Test deux fois à partir du Main. Ce qui se passera, c'est que lorsque viendra le temps d'exécuter ce code, .NET lancera la méthode Test afin de donner un résultat à la variable resultat. On fait alors un appel de la méthode.

Je vais immédiatement déclarer un delegate pour la même situation, comme ça, vous verrez de quoi il en retourne.

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestThread { class Program { //Mon delegate aura exactement la même signature que ma méthode ! delegate bool PremierDelegate(string i); static void Main(string[] args) { //Je crée une variable a qui contiendra la méthode Test. PremierDelegate a = new PremierDelegate(Test); //Au lieu d'appeler Test, je vais appeler a, ce qui me donnera le //même résultat ! bool resultat = a("Ceci est un test qui est négatif !"); bool res2 = a("Positif"); } static public bool Test(string test) { return test.Length < 15; } } }

Bon, dans cet exemple, l'utilisation d'un delegate est carrément inutile, mais ils deviendront rapidement indispensables, surtout lors de la programmation réseau.

Une autre utilisation très fréquente des delegates est la gestion des événements ! Quand vous vous abonnez à un événement, vous refilez tout simplement une méthode à une liste de delegate. Quand on invoque un événement, on appelle toutes les méthodes abonnées. Les événements peuvent faire l'objet d'un autre tutoriel et dépassent les objectifs de ce tutoriel. Si vous voulez plus d'informations, veuillez visiter le site de MSDN en attendant.

Dans cet exemple, je me réfère au concept de signature d'une méthode. Je vais prendre quelques minutes pour vous expliquer.

Les signatures de méthodes

Toute méthode possède une signature. Une signature de méthode, comme dans le monde réel, sert à identifier une méthode de façon unique. Si vous avez déjà fait de la programmation, peut-être avez-vous déjà vu ce concept.

La signature de méthode résout le problème des noms uniques dans le cas de surcharge d'une méthode. Dans les langages autorisant la surcharge de méthode, on se réfère à la signature plutôt qu'au nom de la méthode pour l'appeler. Pour chaque méthode doit correspondre une signature différente des autres. Je vais vous faire un cours en accéléré, donc je vous invite à vous référer à la documentation de MSDN ou alors au Forum du Site du Zéro si vous avez de plus amples questions.

Donc, la signature d'une méthode contient les informations suivantes :

  • L'identificateur de la méthode

  • La séquence des types de la liste des paramètres

Le type de retour ne fait pas partie de la signature ! En effet, ce qui est important de retenir est le Nom de la méthode et les paramètres dans l'ordre.

Ce qui fait que notre méthode static public int Test(string test) a la signature suivante : Test(string);

La définition d'un delegate est un peu différente. Le nom de la méthode et des paramètres sont inutiles. Ce qui compte c'est le type de retour et l'ordre des paramètres selon leur type (pas leur nom). Ainsi, un delegate ne pourra référer qu'une méthode possédant la même définition.

L'autopsie d'un delegate !

Analysons maintenant comment créer ce fameux delegate. Tout d'abord, sachez que le rôle principal d'un delegate est de passer, croyez-le ou non, une méthode en paramètre à une autre méthode. On peut alors aisément imaginer la flexibilité d'un code en permettant à une méthode générique d'appeler une méthode passée en paramètre et d'en afficher le résultat, peu importe les opérations à effectuer. Nous verrons un exemple un peu plus tard. Pour le moment, voici la définition d'un delegate :

[Attribut][Modificateur d'accès]delegatetypeRetourNomDuDelegate (paramètres)

Les cases entourés de crochets sont optionnels. Un attribut, par exemple, sert à injecter des meta-données récupérables à l'exécution du programme. Cela dépasse le cadre de ce cours, mais vous pouvez vous rabattre sur MSDN pour plus d'explications. Les modificateurs d'accès, quant à eux, sont une notion qui devrait vous être connue. Il s'agit de modifier l'accès à l'aide des mots clés public, private, protected, internal, etc.

Voici un exemple :

Je parlais un peu plus haut de la définition d'un delegate. Il faut bien la lui donner cette définition ! On fait généralement cela dans le même espace où l'on déclare les variables globales. Vous verrez dans l'exemple qui suit. Ensuite, on utilise le delegate comme un objet. On peut alors l'utiliser dans les paramètres ou ailleurs si nécéssaire. Ce sera plus clair pour vous avec l'exemple qui suit :

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TestThread { class Program { delegate int Calcul(int i1, int i2); static void Main(string[] args) { //Affichage de la console. Console.WriteLine("Test de delegate"); Console.WriteLine("----------------------"); //On passe à la méthode Afficher la méthode à lancer et les arguments. Afficher(Add, 25, 19); Afficher(Sub, 52, 17); Afficher(Mul, 10, 12); Afficher(Div, 325, 5); //On ne ferme pas la console immédiatement. Console.ReadKey(); } //On fait une méthode générale qui prendra le delegate en paramètre. static void Afficher(Calcul calcul, int i, int j) { Console.WriteLine("{0} {1} {2} = {3}", i, calcul.Method.Name, j, calcul(i, j)); } //Méthodes très simples qui ont toutes un type de retour et des paramètres identiques. static int Add(int i, int j) { return i + j; } static int Sub(int i, int j) { return i - j; } static int Mul(int i, int j) { return i * j; } static int Div(int i, int j) { return i / j; } } }

C'est pas du chinois, mais c'est pas simple, hein ? En effet, ça arrache une grimace la première fois qu'on voit ça, mais en fait, c'est très simple. Comme toutes nos méthodes répondent à la même définition que notre delegate, nous sommes en mesure de toutes les utiliser à l'aide du même. C'est un peu comme de dire qu'un int peut égaler 0 ou bien 12154, car les deux répondent à la même définition, soit être un entier entre int.Min et int.Max.

Ce code, bien que simple est très puissant. Si je voulais ajouter l'opération modulo, il serait TRÈS TRÈS simple de le faire, vous ne trouvez pas ?

Cela conclut notre introduction aux delegates. Il y en a plus que ça à savoir et à comprendre, mais si vous maîtrisez cette partie, c'est excellent. Si vous ne maîtrisez pas bien, je vous recommande de la relire ! Si vous avez fait du C ou du C++, cette fonctionnalité ressemble étrangement aux pointeurs sur fonction. Lorsque l'on fera de la programmation réseau, ces delegates devriendront essentiels ! Courage, c'était vraiment le plus difficile ...

Les threadsUn peu d'histoire

On parle beaucoup de threads ces temps-ci. Les nouveaux processeurs sont des puces conçues afin d'optimiser le traitement de plusieurs threads simultanés. Il y a quelques années, à l'ère du Pentium 4, on ne cessait d'augmenter la fréquence d'horloge afin d'optimiser la vitesse d'un seul coeur. Chaque thread avait un numéro et attendait son tour afin d'être traité.

Ne vous méprenez pas, cela n'a pas changé, mais les processeurs ont commencé à les traiter simultanément, d'abord avec la technologie HT sur les derniers Pentium 4, puis à l'aide de multiples coeurs avec les Core 2 Duo / Quad. Maintenant, il s'agit d'un mélange des deux, soit de multiples coeurs qui appliquent chacun une technologie HT, comme dans le Core i7 d'Intel. Pardonnez-moi, mais je connais très mal les processeurs AMD, étant un utilisateur d'Intel majoritairement :D .

Cela explique un peu l'évolution de la technique de traitement des threads, mais je ne vous ai toujours pas expliqué comment fonctionne le multi-task en Windows.

Le multi-task en Windows

Un ordinateur, ça ne sait faire qu'une seule chose à la fois !

Windows, comme tout bon SE actuel se sert d'une méthode particulière afin de simuler un multi-task. En effet, un processeur ne sait que faire une chose à la fois. La technique est bien simple, il s'agit de créer un système de jeton et de le passer à chaque processus pour un certain temps selon leur priorité.

En ce moment même, vous utilisez votre navigateur préféré pour visiter le site du Zéro, mais cela n'empêche pas votre ordinateur de vaquer à d'autres occupations. Par exemple, vous êtes peut-être en train de copier un fichier, ou même juste en train d'avoir 3 fenêtres ouvertes sur le Bureau en ce moment. Windows doit rafraîchir leur contenu à toutes les x millisecondes afin de créer un sentiment de fluidité chez l'utilisateur.

Donc, suivant cet exemple, Windows aura un jeton à accorder à votre navigateur web pour tant de temps, puis suspendra ses calculs et opérations et donnera le jeton à un autre traitement. Lorsqu'il reviendra au navigateur, celui-ci sera autorisé à continuer ses opérations. Comme ça, tout le monde est content, mais surtout l'utilisateur qui désire ouvrir Google Chrome en même temps que MSN et Word, ainsi que Visual Studio 2010. Ne riez pas, c'est pas mal le scénario actuel de mon PC en ce moment... Tout ça pour dire qu'on commence la section sur les threads pour de vrai !

Les threads, enfin !

Les threads sont des exécutions que l'on sépare de l'exécution principale pour les raisons suivantes :

  • Tâche exigeante (gros calculs, gros traitements, etc)...

  • Tâche détachée (impression, recherche, etc)...

  • Tâche bloquante (ça sent le réseau ici !)...

Créer un thread dans ces cas est utile afin de créer un certain parallélisme dans les exécutions.

Les threads rendent un code beaucoup plus complexe, non seulement à l'écriture, mais aussi au débogage ! Oh là là, que de frustration passées à programmer avec les threads. Cela dit, ils apportent énormément de puissance à une application ! Cette complexité vient du fait que le système de jeton est imprévisible ! Par exemple, il pourrait passer 3 secondes sur un thread A, mais 2 secondes sur un thread B. Bien-sûr, on ne parle pas de secondes ici, mais bien de nano-secondes. Un autre point frustrant sera que lors du déboggage, vous pourriez vous retrouver dans une méthode complètement différente de celle qui vous intéresse en un clin d'oeil, justement à cause que le système de jeton a changé de thread et vous a projeté dans la méthode qu'il exécute au moment même.

Le cas des tâches bloquantes...

Une application Windows Forms est amenée à se rafraîchir assez fréquemment. Lors de grosses opérations ou d'opérations synchrones qui bloquent, tout le temps de calcul est alloué à ces tâches et non plus au rafraîchissement. Après un certain temps, Windows déclare l'application comme "Ne répondant plus". Si vous planifiez de distribuer votre application, ce comportement est inacceptable, vous en conviendrez. C'est dans ce type de cas qu'on utilisera les threads. Imaginez afficher une belle animation sur un Splash Screen alors que les ressources sont en chargement en arrière plan.

Les différents types de thread

Il est possible de créer deux types de threads, bien que cela revienne au même. Lors de l'instanciation de la classe Thread, il est possible de garder la référence de l'objet, ou de la laisse flotter. Si on ne la récupère pas, on appellera ce thread "indépendant". Il sera créé, puis lancé immédiatement. Comme on ne gardera pas la référence, on ne pourra pas contrôler ce thread du tout. Il fera ce qu'il a à faire, sans que l'on puisse intervenir (sauf en utilisant quelques primitives de synchronisation que nous verrons plus tard).

Voici comment déclarer un thread indépendant :

new Thread(fonction).Start();

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Les Threads en .NET

Prix sur demande