Concurrence et multithreading en C# : Guide complet

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Concurrence et multithreading en C# : Guide complet

Les applications modernes nécessitent des performances élevées et une réactivité optimale. La concurrence (concurrency) et le multithreading (multithreading) en C# sont des outils clés permettant d'exécuter plusieurs tâches simultanément, d'utiliser efficacement les processeurs multicœurs et d'améliorer l'expérience utilisateur. Dans cet article, nous allons analyser les concepts fondamentaux, depuis les threads classiques jusqu'à l'async/await moderne et Parallel LINQ.

Fondamentaux du multithreading : la classe Thread et le pool de threads

Le multithreading en C# commence avec la classe System.Threading.Thread. Chaque thread représente un chemin d'exécution distinct au sein d'un processus. Cependant, la création manuelle de threads est une opération coûteuse. Pour optimiser cela, on utilise le pool de threads (ThreadPool), qui gère la réutilisation des threads.

Exemple de création manuelle d'un thread :

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(DoWork);
        thread.Start();
        Console.WriteLine("Le thread principal travaille...");
        thread.Join(); // Attend la fin
    }

    static void DoWork()
    {
        Console.WriteLine("Le thread exécute le travail.");
    }
}

Pour une utilisation plus simple, ThreadPool est recommandé :

ThreadPool.QueueUserWorkItem(state => {
    Console.WriteLine("Tâche dans le pool de threads.");
});

Important : La classe Thread possède une API de bas niveau et ne prend pas en charge le retour de résultat. Pour les applications modernes, utilisez Task.

Task et TPL (Task Parallel Library)

La bibliothèque TPL (System.Threading.Tasks) fournit une abstraction de haut niveau — Task. Task représente une opération asynchrone qui peut retourner une valeur. C'est la base de la concurrence moderne en C#.

Exemple de Task sans valeur de retour :

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Task task = Task.Run(() => DoWork());
        await task;
        Console.WriteLine("Terminé.");
    }

    static void DoWork()
    {
        Console.WriteLine("Travail dans Task.");
    }
}

Task avec valeur de retour :

Task<int> task = Task.Run(() => {
    int result = 0;
    for (int i = 0; i < 1000000; i++) result += i;
    return result;
});
int sum = await task;
Console.WriteLine($"Somme : {sum}");

TPL inclut également Parallel.For et Parallel.ForEach pour le traitement parallèle des collections :

Parallel.For(0, 10, i => {
    Console.WriteLine($"Itération {i} sur le thread {Thread.CurrentThread.ManagedThreadId}");
});

Async/await : programmation asynchrone

Les mots-clés async et await simplifient l'écriture de code asynchrone, le rendant similaire au code synchrone. Ils ne créent pas de nouveaux threads, mais basculent efficacement le contexte d'exécution, libérant le thread pour d'autres tâches.

Exemple de méthode asynchrone :

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string data = await FetchDataAsync("https://example.com");
        Console.WriteLine(data);
    }

    static async Task<string> FetchDataAsync(string url)
    {
        using HttpClient client = new HttpClient();
        string result = await client.GetStringAsync(url);
        return result;
    }
}

Règles d'utilisation :

  • Une méthode avec async doit retourner Task, Task<T> ou void (uniquement pour les gestionnaires d'événements).
  • Utilisez await uniquement à l'intérieur d'une méthode marquée async.
  • Évitez async void sauf pour les gestionnaires d'événements.

Blogs

Book Recommendations