CodeGym /Cours /C# SELF /Lancement de threads avec la classe Thread

Lancement de threads avec la classe Thread

C# SELF
Niveau 55, Leçon 1
Disponible

1. Introduction

Si on imagine un processus comme un supermarché, les threads (thread) sont les caissiers sur différentes caisses qui servent les clients en même temps. Tous les caissiers travaillent dans le même magasin, mais chacun accomplit sa tâche en parallèle, ce qui rend le travail plus rapide et efficace. Les threads à l'intérieur d'un processus permettent d'exécuter plusieurs tâches simultanément, en partageant des ressources communes et en coordonnant le travail au sein d'une même application. Le principal avantage — les threads peuvent réellement fonctionner en parallèle si le processeur supporte le multitâche.

Besoin pratique des threads

Pourquoi lancer plusieurs threads ? Voici quelques situations concrètes :

  • Vous développez une application avec interface graphique et vous ne voulez pas qu'elle "bloque" pendant une opération longue.
  • Vous devez télécharger plusieurs fichiers en même temps.
  • Dans un jeu, les ennemis doivent réfléchir indépendamment les uns des autres.

Étonnamment, beaucoup de programmes souffrent encore de "blocages" à cause d'une mauvaise utilisation des threads. Aujourd'hui on va apprendre à éviter ces malentendus.

La classe Thread : la base du multithreading manuel

La classe Thread est un dinosaure du programmation multithread en .NET. Malgré l'apparition d'outils plus modernes (Task, async/await), travailler avec Thread a toujours du sens, surtout si vous voulez sentir que vous contrôlez les threads "à la main".

Schéma de création d'un thread

  1. Créer un objet de la classe Thread, en lui passant la méthode qui sera exécutée dans le thread.
  2. Lancer le thread avec la méthode Start().
  3. (Optionnel) Observer ce qui se passe — tout peut fonctionner en parallèle !

2. Lancer un thread avec Thread

Essayons de ressentir concrètement la magie du parallélisme. Ajoutons à notre application fictive une petite classe qui compte jusqu'à un certain nombre et affiche la progression. On va apprendre à lancer ce travail dans un thread séparé.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("Le thread principal a démarré !");

        // On crée l'objet thread en indiquant la méthode à exécuter
        Thread workerThread = new Thread(CountToTen);

        // On lance le nouveau thread
        workerThread.Start();

        // Le thread principal fait aussi quelque chose : il affiche des points...
        for (int i = 0; i < 5; i++)
        {
            Console.Write(".");
            Thread.Sleep(500); // Pause pour la démonstration
        }

        Console.WriteLine("\nLe thread principal est terminé !");
    }

    static void CountToTen()
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine($"[Thread] On compte : {i}");
            Thread.Sleep(400);
        }
        Console.WriteLine("[Thread] Terminé !");
    }
}

Que se passe-t-il ?

Vous verrez dans la console que les points et "On compte : X" apparaissent mélangés. C'est le premier signe du multithreading ! Le thread principal affiche ses points, et le nouveau thread compte jusqu'à 10. Ils ne se gênent pas, comme deux musiciens dans un groupe : l'un joue de la batterie, l'autre du piano. Chacun son son, et ensemble ça fait de la musique.

3. Comment passer des données au thread ?

Parfois le thread doit savoir non seulement quoi faire, mais aussi avec quoi travailler. Si la méthode du thread prend des paramètres, comment les lui fournir ?

Option 1 : Avec une lambda (méthode anonyme)

int bounds = 7;
Thread t = new Thread(() => CountToNumber(bounds));
t.Start();
static void CountToNumber(int n)
{
    for (int i = 1; i <= n; i++)
    {
        Console.WriteLine($"[Thread] {i} / {n}");
        Thread.Sleep(300);
    }
}

Ici on enveloppe l'appel de la méthode souhaitée dans une lambda pour passer les paramètres. C'est une pratique très répandue, car Thread attend une méthode sans paramètres (ThreadStart).

Option 2 : Utiliser ParameterizedThreadStart

On peut utiliser le délégué spécial ParameterizedThreadStart, qui prend un seul paramètre de type object.

Thread t = new Thread(CountToNumberObject);
t.Start(12);

static void CountToNumberObject(object? n)
{
    int max = (int)n!;
    for (int i = 1; i <= max; i++)
    {
        Console.WriteLine($"[Thread] {i} / {max}");
        Thread.Sleep(200);
    }
}

Oui, le type du paramètre est object, donc il faudra caster. Pas idéal, mais ça marche ! Cependant le C# moderne préfère la variante avec lambda.

4. Gérer la vie d'un thread

Voyons quelles fonctionnalités utiles la classe Thread fournit.

Propriété / Méthode Usage
Thread.Start()
Démarre le thread (la méthode indiquée lors de la création)
Thread.Join()
Attend la fin du thread (bloque le thread appelant jusqu'à la fin de l'autre thread)
Thread.IsAlive
Indique si le thread fonctionne actuellement (true/false)
Thread.Name
Permet de donner un nom au thread (utile pour le debugging)
Thread.CurrentThread
Obtenir l'objet représentant le thread courant
Thread.Sleep(ms)
Arrête le thread courant pendant ms millisecondes

Exemple : On attend la fin d'un thread

Parfois il faut que le thread principal attende la fin du thread auxiliaire.

Thread t = new Thread(() =>
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine($"[Second thread] {i}");
        Thread.Sleep(300);
    }
});
t.Start();

Console.WriteLine("[Thread principal] On attend la fin du second thread...");
t.Join(); // Le thread principal attend ici

Console.WriteLine("[Thread principal] Le second thread est terminé !");

Sans Join() le programme pourrait se terminer alors que le thread fonctionne encore. Avec Join() le thread principal attend patiemment que tout soit fini.

5. Astuces utiles

Nommer les threads : pour ne pas se perdre

Pour le debugging, on peut donner des noms aux threads :

Thread t = new Thread(() =>
{
    Console.WriteLine($"Ceci s'exécute dans le thread : {Thread.CurrentThread.Name}");
});
t.Name = "Thread-Compteur";
t.Start();

Cela aide quand il y a beaucoup de threads et que chacun fait son boulot.

Limitations et avenir réel

Il faut dire tout de suite : dans les applications modernes, gérer manuellement les threads via Thread est rare. En pratique on utilise souvent des outils plus puissants et intelligents (Task, async/await), qui seront vus plus tard. Mais comprendre les bases du fonctionnement des threads est important pour :

  • Comprendre les coulisses de C# et .NET.
  • Les entretiens techniques (on peut vous demander d'expliquer la différence entre Thread et Task).
  • Diagnostiquer et résoudre des problèmes complexes dans de grandes applications "héritées".

Schéma final : cycle de vie d'un thread

stateDiagram-v2
    [*] --> New: Thread créé
    New --> Running: Start()
    Running --> Stopped: Méthode terminée
    Stopped --> [*]

Maintenant vous savez créer et lancer des threads en C#. Vous n'êtes plus seulement un passager dans le train, mais le mécanicien qui contrôle plusieurs wagons ! Devant vous : le cycle de vie du thread, sa gestion, la synchronisation et de nouveaux horizons du parallélisme.

6. Erreurs typiques et astuces quand on travaille avec Thread

Erreur n°1 : ne pas démarrer le thread.
On crée souvent un objet Thread mais on oublie d'appeler Start(). Résultat : le thread ne commence pas à travailler, et il est difficile de comprendre pourquoi.

Erreur n°2 : modifier des données partagées sans synchronisation.
Si plusieurs threads travaillent sur la même variable sans protection, attendez-vous à des problèmes ! C'est comme si deux caissiers donnaient la monnaie depuis une même boîte — rapidement ça devient le bazar et il y aura des erreurs.

Erreur n°3 : utiliser des méthodes obsolètes et dangereuses.
N'utilisez pas Thread.Suspend(), Thread.Resume() et similaires — elles sont considérées comme dangereuses et obsolètes. Gérez le cycle de vie des threads autrement.

Erreur n°4 : exceptions non gérées à l'intérieur des threads.
Si une exception se produit dans un thread et n'est pas catchée, le thread va terminer et le thread principal peut ne pas le remarquer ! Entourez le code du thread avec un bloc try-catch pour capturer et logger les erreurs.

Thread t = new Thread(() =>
{
    try
    {
        // ... votre code
    }
    catch (Exception ex)
    {
        // On log l'erreur
        Console.WriteLine($"[Thread] Erreur : {ex.Message}");
    }
});
t.Start();
2
Mission
C# SELF, niveau 55, leçon 1
Bloqué
Attente de la fin du thread (Join)
Attente de la fin du thread (Join)
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION