Gestion des processus et des threads en C#
Apprenez la gestion des processus et des threads en C# pour contrôler l’exécution et les ressources système.
Dans les applications modernes, les concepts de processus et de thread sont utilisés pour exécuter plusieurs tâches simultanément. C# et .NET offrent des API puissantes pour gérer ces deux structures. Un processus est une instance d’un programme en cours d’exécution, tandis qu’un thread est un flux d’exécution parallèle à l’intérieur de ce processus. Cet article explique les bases du démarrage de processus, de la gestion des threads, de la synchronisation et de l’exécution parallèle.
Qu’est-ce qu’un Processus ?
Chaque application en cours d’exécution est représentée par le système d’exploitation comme un processus.
Un processus possède sa propre mémoire, ses ressources et ses threads actifs.
En .NET, la gestion des processus se fait via la classe System.Diagnostics.Process.
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
// Démarrer un nouveau processus Bloc-notes
Process.Start("notepad.exe");
// Lister tous les processus en cours
foreach (var proc in Process.GetProcesses())
{
Console.WriteLine($"{proc.ProcessName} - ID: {proc.Id}");
}
}
}
La méthode Process.Start() lance une nouvelle application,
tandis que GetProcesses() récupère tous les processus en cours d’exécution.
Travailler avec un Processus
Vous pouvez interagir avec une application en cours via son objet processus : l’arrêter, l’attendre ou la suspendre.
using System;
using System.Diagnostics;
using System.Threading;
class Program
{
static void Main()
{
// Démarrer le Bloc-notes
var p = Process.Start("notepad.exe");
// Attendre 3 secondes
Thread.Sleep(3000);
// Fermer
p.Kill();
Console.WriteLine("Bloc-notes fermé.");
}
}
Dans cet exemple, le processus est arrêté après 3 secondes.
Attention : la méthode Kill() met fin au processus de manière forcée,
ce qui peut entraîner une perte de données non enregistrées.
Qu’est-ce qu’un Thread ?
Un thread est une unité d’exécution au sein d’un processus. Par défaut, chaque application démarre avec un thread principal. Des threads supplémentaires peuvent être créés pour exécuter plusieurs tâches en parallèle.
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread t = new Thread(Saluer);
t.Start();
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Le thread principal est en cours d’exécution...");
Thread.Sleep(500);
}
t.Join(); // Attendre la fin du thread
Console.WriteLine("Programme terminé.");
}
static void Saluer()
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Le thread de salutation est en cours d’exécution...");
Thread.Sleep(400);
}
}
}
Dans cet exemple, deux threads s’exécutent simultanément.
La méthode Join() attend la fin du thread.
Priorité et État d’un Thread
Chaque thread possède une valeur Priority et un état ThreadState.
La priorité d’un thread peut influencer la planification du processeur.
Thread t1 = new Thread(() =>
{
Console.WriteLine("Tâche à faible priorité.");
});
t1.Priority = ThreadPriority.Lowest;
Thread t2 = new Thread(() =>
{
Console.WriteLine("Tâche à haute priorité.");
});
t2.Priority = ThreadPriority.Highest;
t1.Start();
t2.Start();
Console.WriteLine($"État de t1 : {t1.ThreadState}");
Console.WriteLine($"État de t2 : {t2.ThreadState}");
Remarque : Le système d’exploitation prend en compte la priorité des threads, mais cela dépend du planificateur et n’est pas garanti.
Sécurité et Synchronisation des Threads
Lorsque plusieurs threads accèdent à la même ressource (par exemple une variable, une liste ou un fichier), une synchronisation est nécessaire. Sinon, une condition de concurrence peut se produire.
using System;
using System.Threading;
class Program
{
static int compteur = 0;
static object verrou = new object();
static void Main()
{
Thread t1 = new Thread(Incremente);
Thread t2 = new Thread(Incremente);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine($"Résultat : {compteur}");
}
static void Incremente()
{
for (int i = 0; i < 1000; i++)
{
lock (verrou)
{
compteur++;
}
}
}
}
Le mot-clé lock garantit qu’un seul thread à la fois peut accéder au bloc protégé,
évitant ainsi toute incohérence de données.
Gestion Légère des Threads avec ThreadPool
Le ThreadPool réduit le coût de création de nouveaux threads pour des tâches de courte durée. .NET réutilise automatiquement les threads disponibles à partir d’un pool partagé.
using System;
using System.Threading;
class Program
{
static void Main()
{
for (int i = 0; i < 5; i++)
{
int id = i;
ThreadPool.QueueUserWorkItem(_ =>
{
Console.WriteLine($"Tâche {id} démarrée (Thread ID : {Thread.CurrentThread.ManagedThreadId})");
Thread.Sleep(500);
});
}
Console.WriteLine("Toutes les tâches ont été soumises.");
Thread.Sleep(2000); // Attendre la fin du ThreadPool
}
}
Le ThreadPool est idéal pour les tâches courtes et répétitives (par exemple les requêtes HTTP ou la journalisation).
Différences entre Processus et Thread
| Propriété | Processus | Thread |
|---|---|---|
| Définition | Instance d’un programme en cours d’exécution | Unité d’exécution parallèle dans un processus |
| Utilisation de la mémoire | Possède sa propre mémoire | Partage la mémoire du processus |
| Indépendance | Fonctionne indépendamment | Dépend du processus |
| Coût de création | Élevé | Faible |
| Communication | Nécessite une communication inter-processus (IPC) | Facile via la mémoire partagée |
Exemple : Téléchargement de Fichiers en Parallèle
Dans l’exemple ci-dessous, chaque téléchargement de fichier s’exécute dans un thread distinct, permettant le téléchargement simultané de plusieurs fichiers.
using System;
using System.Net;
using System.Threading;
using System.IO;
class Program
{
static void Main()
{
string[] fichiers = {
"https://example.com/fichier1.jpg",
"https://example.com/fichier2.jpg",
"https://example.com/fichier3.jpg"
};
foreach (var url in fichiers)
{
new Thread(() => TelechargerFichier(url)).Start();
}
Console.WriteLine("Téléchargements commencés...");
}
static void TelechargerFichier(string url)
{
string nom = Path.GetFileName(url);
using var client = new WebClient();
client.DownloadFile(url, nom);
Console.WriteLine($"{nom} téléchargé. (Thread : {Thread.CurrentThread.ManagedThreadId})");
}
}
Cet exemple montre que l’exécution parallèle peut considérablement accélérer les opérations d’entrée/sortie.
Performances et Recommandations
- Créer trop de threads peut consommer beaucoup de ressources système.
- Des blocs
locktrop longs réduisent les performances ; gardez-les aussi courts que possible. - Pour les tâches intensives en CPU, utilisez la
Task Parallel Library (TPL). - Pour les tâches I/O, le modèle asynchrone
async/awaitest plus efficace. - Les processus ne partagent pas la mémoire ; utilisez des tuyaux nommés (named pipes), des fichiers ou des sockets pour la communication inter-processus.
TL;DR
- Processus : Instance d’un programme avec son propre espace mémoire.
- Thread : Flux d’exécution parallèle dans un processus partageant la mémoire.
- ThreadPool : Idéal pour de nombreuses tâches courtes.
- lock : Assure la synchronisation et la cohérence des données.
- Trop de threads entraînent un gaspillage de ressources ; privilégiez
Taskouasyncpour plus d’efficacité.
Articles connexes
Bases de la programmation asynchrone en C# (async/await)
Apprenez async et await en C# pour créer des applications réactives avec des tâches asynchrones et des exemples pratiques.
Bibliothèque parallèle TPL et programmation parallèle en C#
Apprenez la Task Parallel Library et la programmation parallèle en C# avec Task, Parallel et des exemples pratiques.
Flux asynchrones en C# (IAsyncEnumerable)
Apprenez les flux asynchrones en C# avec IAsyncEnumerable pour traiter les données progressivement avec des exemples.