GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Les threads en C#

0. Introduction


Les Threads Windows en C#

Préambule

Lorsque vous exécutez un programme (les fameux .exe vous savez...), le système d'exploitation commence par créer un processus. C'est un espace totalement isolé (normalement) dans la mémoire auquel sont affectées des ressources requises pour l'exécution du programme (descripteurs de fichiers, ram, pile des variables, tas, ...).

Un thread peut être considéré comme une partie d'un processus, avec une isolation plus faible. En particulier, un thread peut partager de la mémoire avec les autres threads de l'application. L'usage classique du multithreading est de permettre à l'interface graphique de fonctionner parfaitement sur un thread pendant que les travaux plus lourds seront exécutés par d'autres threads, les worker threads.

Globalement on peut se souvenir que :

Lors du démarrage du programme principal, le processus génère un thread, qui va réellement composer l'application. Ce thread principal peut lui-même générer de nouveaux threads auxquels il va assigner des tâches. Dans le cadre d'une architecture mono processeur, chaque thread va recevoir un quantum du temps processeur pour réaliser une partie de son exécution, simulant l'exécution en parallèle. Sur une architecture multi processeurs (ou multicores), chaque thread va être lancé sur un coeur (selon le nombre de threads et de coeurs disponibles) pour obtenir une véritable exécution en parallèle.

Avantages des threads

Inconvénients des threads

Premier exemple simple

Dans cet exemple, nous créons deux threads réalisant simplement des boucles avec un affiche de l'incrément en cours de traitement.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace threads_console
{
    class Program
    {
        static void Main(string[] args)
        {
            // Création d'un thread avec la fonction T1_doWork()
            Thread t1 = new Thread(T1_doWork);
            // Création d'un thread avec la fonction T2_doWork()
            Thread t2 = new Thread(T2_doWork);

            // Démarrage des threads
            t1.Start();
            t2.Start();
            // Même chose avec le thread principal
            mainThreadJob();
        }

        // Fonction appelée par le thread T1
        static void T1_doWork()
        {
            for (int i = 0; i < 101; i++)
            {
                Console.Write("A{0} ", i);
            }
        }

        // Fonction appelée par le thread 21
        static void T2_doWork()
        {
            for (int i = 0; i < 101; i++)
            {
                Console.Write("B{0} ", i);
            }
        }

        // Fonction appelée par le thread principal
        static void mainThreadJob()
        {
            for (int i = 0; i < 101; i++)
            {
                Console.Write("M{0} ", i);
            }
        }
    }
}
	
Le résultat obtenu est le suivant :

A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 A17 A18 A19 A20
A21 A22 A23 A24 A25 A26 A27 A28 A29 A30 A31 A32 A33 A34 A35 A36 A37 A38 A39 A40
A41 A42 A43 A44 A45 A46 A47 A48 A49 A50 A51 A52 A53 A54 A55 A56 A57 A58 A59 A60
A61 A62 A63 A64 A65 A66 A67 A68 A69 A70 A71 A72 A73 A74 A75 A76 A77 A78 A79 A80
A81 A82 A83 A84 A85 A86 A87 A88 A89 A90 A91 A92 A93 M0 M1 M2 M3 M4 B0 B1 B2 B3
B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 B20 B21 B22 B23 B24
B25 B26 B27 B28 B29 B30 B31 B32 B33 B34 B35 B36 B37 B38 B39 B40 B41 B42 B43 B44
B45 B46 B47 B48 B49 B50 B51 B52 B53 B54 B55 B56 B57 B58 B59 B60 B61 B62 B63 B64
B65 B66 B67 B68 B69 B70 B71 B72 B73 B74 B75 B76 B77 B78 B79 B80 B81 B82 B83 B84
B85 B86 B87 B88 B89 B90 B91 B92 B93 B94 B95 B96 B97 B98 B99 B100 M5 M6 M7 M8 M9
M10 M11 M12 M13 M14 M15 M16 M17 M18 M19 M20 M21 M22 M23 M24 M25 M26 M27 M28 M29
M30 M31 M32 M33 M34 M35 M36 M37 M38 M39 M40 M41 M42 M43 M44 M45 M46 M47 M48 M49
M50 M51 M52 M53 M54 M55 M56 M57 M58 M59 M60 M61 M62 M63 M64 M65 M66 M67 M68 M69
M70 M71 M72 M73 M74 M75 M76 M77 M78 M79 M80 M81 M82 M83 M84 M85 M86 M87 M88 M89
M90 M91 M92 M93 M94 M95 M96 M97 M98 M99 M100 A94 A95 A96 A97 A98 A99 A100
	
Celui-ci sera probablement différent chez vous et différent d'une exécution ultérieure de part la manière dont le système ordonnance les threads.

Deuxième exemple : threads avec variable locale

Dans cet exemple, nous allons voir que les threads disposent de leur espace mémoire séparé en faisant exécuter une même fonction par deux threads. Pour que l'exemple soit effectif, il faut que le travail réalisé par le thread soit suffisamment long pour que l'on puisse profiter du multi-thread. Sinon le temps requis pour créer le contexte du thread provoque simplement un fonctionnement en séquence.


using System;
using System.Threading;

namespace threads_console2
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread th = new Thread(thread_doWork);
            th.Start();
            thread_doWork();
        }

        // Fonction exécutée à la fois par le thread principal et le thread dynamiquement créé.
        // La boucle compte jusqu'à 100 pour avoir le temps de constater le fonctionnement multi threads
        static void thread_doWork()
        {
            for (int i = 0; i < 100; i++)
            {
                Console.Write("{0} ", i);
            }
        }
    }
}
	
Le résultat est le suivant :

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 0 1 2 3
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
93 94 95 96 97 98 99 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
Nous pouvons bien voir que l'affichage des valeurs en sortie affiche 2 fois le cycle de 100 chiffres et non 1 fois. Les piles de variables sont donc bien séparées.

Accès rapides

Yep
Nothing here yet...