CodeGym /Cours /C# SELF /Priorités des threads et types de threads

Priorités des threads et types de threads

C# SELF
Niveau 55, Leçon 3
Disponible

1. Introduction

Imaginez que les threads sont des gens dans une file pour une glace. Certains attendent calmement, d'autres font du bruit ("Mon train part bientôt, laissez-moi passer!"), et le vendeur semble entendre tout le monde, mais parfois il sert quelqu'un hors tour — par exemple une maman avec un enfant qui pleure. Les priorités des threads, c'est un peu la même histoire.

En C# (plus précisément en .NET) chaque thread a une priorité — un indice pour l'OS sur l'importance relative de ce thread. Ce n'est pas une loi stricte pour l'OS (personne ne garantit que le thread le plus prioritaire aura toujours toute l'attention), mais généralement les threads avec une priorité plus élevée obtiennent plus de temps CPU.

Où c'est vraiment utile ?

  • Dans les interfaces utilisateur : par exemple, la mise à jour de l'écran ne doit pas être ralentie par de longs calculs à faible priorité.
  • Dans les jeux : le rendu d'une frame est plus important que la génération de la carte en arrière-plan.
  • Dans les tâches avec contrainte de temps : contrôle de matériel, traitement de signaux, etc.

2. Priorités des threads : comment ça marche

En C#, c'est simple de travailler avec la priorité d'un thread — l'objet Thread a la propriété Priority. Elle prend des valeurs de l'énumération ThreadPriority :

Valeur Description
Lowest
Priorité la plus basse
BelowNormal
En dessous de la normale
Normal
Normal (par défaut)
AboveNormal
Au-dessus de la normale
Highest
Priorité la plus haute

Exemple de code :

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread lowPriorityThread = new Thread(PrintLowPriority);
        Thread highPriorityThread = new Thread(PrintHighPriority);

        lowPriorityThread.Priority = ThreadPriority.Lowest;
        highPriorityThread.Priority = ThreadPriority.Highest;

        lowPriorityThread.Start();
        highPriorityThread.Start();
    }

    static void PrintLowPriority()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Priorité basse : " + i);
            Thread.Sleep(10); // Ajoutons une pause pour voir la différence
        }
    }

    static void PrintHighPriority()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Priorité haute : " + i);
            Thread.Sleep(10);
        }
    }
}

À noter

En pratique, surtout sur les systèmes multi‑cœurs modernes et dans l'environnement géré .NET, la priorité est juste une recommandation pour l'OS. L'OS (et .NET) essaient d'allouer plus de temps au thread à haute priorité, mais il n'y a pas de garantie absolue. Par exemple, si votre thread monopolise le CPU avec la priorité Highest, l'interface peut commencer à ramer et les autres threads en pâtiront.

3. Changer les priorités des threads

Threads et priorités

graph LR
    A[Thread 1 - Lowest] -->|Moins de temps CPU| OS(Système d'exploitation)
    B[Thread 2 - Normal] --> OS
    C[Thread 3 - Highest] -->|Plus de temps CPU| OS
    OS --> CPU(Processeur)

Pourquoi changer la priorité d'un thread ?

On pourrait penser qu'il suffit de mettre Highest partout, mais c'est une mauvaise idée ! Imaginez si tous les clients dans le magasin commençaient à crier "Servez-moi d'abord!".

Changez la priorité seulement quand c'est justifié :

  • Un thread de fond pour écrire des logs — on peut mettre BelowNormal ou Lowest.
  • Un thread qui gère l'entrée utilisateur — AboveNormal.
  • Tâches CPU‑intensives non critiques côté temps — priorité basse.

Astuce : Si l'application commence à ramer, vérifiez s'il n'y a pas des threads "impolis" à haute priorité qui empêchent les autres de s'exécuter correctement !

4. Types (catégories) de threads en .NET

En C#, on distingue traditionnellement deux types de threads : foreground et background. Étonnamment, "background" n'est pas forcément ce qui "travaille en arrière‑plan". On va clarifier ça.

Foreground threads (principaux)

  • Par défaut, tous les threads que vous créez sont foreground.
  • Tant qu'il y a au moins un foreground thread vivant dans le process, l'application ne terminera pas, même si tout le reste est "mort".
  • Exemple : le thread principal du programme (Main), ainsi que tous les threads créés via new Thread() sans modification.

Background threads (secondaires)

  • Si tous les foreground threads sont terminés et qu'il ne reste que des background threads, le process se termine et ces threads sont interrompus sans préavis.
  • Utilisés pour des tâches secondaires : logging, envoi de métriques en arrière‑plan, etc.
  • Pour rendre un thread background : définissez la propriété IsBackground à true :
Thread t = new Thread(SomeMethod);
t.IsBackground = true;
t.Start();

Démonstration de la différence

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread backgroundThread = new Thread(() =>
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("Le thread background travaille... " + i);
                Thread.Sleep(500);
            }
            Console.WriteLine("Le thread background EST TERMINE.");
        });

        backgroundThread.IsBackground = true; // On rend le thread background !

        backgroundThread.Start();

        Console.WriteLine("Main termine son travail dans 1 seconde.");
        Thread.Sleep(1000); // On attend une seconde
        Console.WriteLine("Main est terminé. Que va-t-il arriver au background thread ?");
    }
}

Ce que vous verrez :
Main peut se terminer et le thread background sera "coupé" en plein milieu. C'est pratique pour des tâches qui ne doivent pas empêcher la fermeture de l'application.

5. Types de threads

ThreadPool (Pool de threads)

  • ThreadPool est un mécanisme qui gère un pool de threads pour éviter d'en créer trop.
  • On l'utilise quand il y a beaucoup de petites tâches : traitement parallèle de requêtes, opérations asynchrones.
  • Les threads du pool sont toujours background — ils ne bloquent pas la fin de l'application.

Exemple : exécuter du code dans le ThreadPool

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        ThreadPool.QueueUserWorkItem(DoWorkInThreadPool, "tâche de fond");
        Console.WriteLine("Main termine son travail.");
        Thread.Sleep(500);
    }

    static void DoWorkInThreadPool(object? state)
    {
        Console.WriteLine("Thread du pool : " + state);
        Thread.Sleep(1000); // Essayons de dormir
        Console.WriteLine("Le thread du pool est terminé !");
    }
}

À noter :
Si Main se termine plus tôt, le thread du pool peut être interrompu. Si vous avez besoin d'une garantie de fin, utilisez des foreground threads ou attendez explicitement.

Évolution du multithreading : Task, async/await

De nos jours on crée rarement des threads manuellement (Thread). On utilise plutôt Task et les méthodes asynchrones. Points importants :

  • Les threads du pool et les tâches Task tournent sur des background threads.
  • Pour la plupart des scénarios, il n'est pas nécessaire de changer la priorité — suivez le bon sens et les meilleures pratiques.

6. Nuances utiles

Propriétés et comportement des threads

Propriété Foreground Thread Background Thread ThreadPool Thread
IsBackground
false par défaut true true
Fin de l'application L'application NE se termine PAS tant qu'au moins un foreground thread est vivant L'application se terminera si tous les foreground threads sont terminés L'application se terminera dès que Main sera terminé
Contrôle Contrôle complet Contrôle complet Pas de contrôle direct
Où utilisé Tâches longues et importantes (par ex. serveur DB) Tâches non critiques, opérations de fond, logging Tâches courtes, Task, opérations asynchrones
Priorité On peut définir On peut définir Pas recommandé de modifier

Astuces et remarques pas évidentes

  • On peut changer la priorité seulement pour les threads "normaux" (Thread), pas pour les tâches du pool (Task, ThreadPool).
  • Les threads du pool ont toujours la priorité Normal (on ne peut pas la changer).
  • Task et async/await sont l'approche moderne : les questions de priorité et d'exécution en arrière‑plan sont gérées "derrière le rideau".

7. Erreurs typiques avec les priorités et types de threads

Erreur n°1 : abuser des priorités.
Donner Highest ou Lowest à tous les threads sans raison n'apporte rien de bon. Ça peut casser l'équilibre d'exécution et réduire la réactivité de l'app.

Erreur n°2 : fin implicite des background threads.
Si un travail important (par ex. sauvegarde de données) s'exécute dans un background thread et que vous n'attendez pas sa fin, vous risquez de perdre des données. Les background threads sont interrompus automatiquement à la fin du process.

Erreur n°3 : attentes excessives vis‑à‑vis de la priorité.
La priorité d'un thread (Priority) est juste une recommandation pour l'OS, pas une garantie que le thread sera exécuté en premier.

2
Mission
C# SELF, niveau 55, leçon 3
Bloqué
Définir et vérifier la priorité des threads
Définir et vérifier la priorité des threads
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION