Gestion des files d'attente de tâches en C# : guide pratique

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

Introduction : pourquoi les files d'attente de tâches en C# ?

\n

Dans le développement moderne en C# (.NET), il est souvent nécessaire d'organiser un traitement asynchrone des données. Un exemple classique est un serveur web qui reçoit des requêtes et doit les traiter en arrière-plan sans bloquer le thread principal. Ou une application qui télécharge des fichiers depuis Internet et traite en parallèle ceux déjà téléchargés. Pour de tels scénarios, le pattern « Producer-Consumer » (producteur-consommateur) est idéal, et sa base est la file d'attente de tâches.

\n

La file d'attente de tâches permet de transférer en toute sécurité le travail d'un thread (ou d'une opération asynchrone) à un autre, en synchronisant l'accès aux données partagées. Dans cet article, nous examinerons trois méthodes principales pour implémenter des files d'attente en C# : ConcurrentQueue, BlockingCollection et le moderne System.Threading.Channels. Chacune convient à différents scénarios, et nous les analyserons avec des exemples pratiques.

\n\n

1. ConcurrentQueue — file d'attente thread-safe de base

\n

ConcurrentQueue<T> est la file d'attente thread-safe la plus simple de l'espace de noms System.Collections.Concurrent. Elle utilise des algorithmes sans verrou (comparaison et échange, CAS) pour garantir des performances élevées. Cependant, elle ne bloque pas le thread lorsque la file est vide — vous devez implémenter l'attente vous-même.

\n\n

Exemple : téléchargement de données depuis plusieurs sources

\n

Supposons que nous ayons une tâche : télécharger 10 pages web et les traiter. Un thread (Producer) ajoute des URL à la file, et plusieurs threads (Consumers) les récupèrent et les téléchargent.

\n
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class ConcurrentQueueExample\n{\n    public static async Task RunAsync()\n    {\n        var queue = new ConcurrentQueue<string>();\n        var urls = new[] { "https://example.com", "https://dotnet.microsoft.com" };\n\n        // Producer : ajout des tâches\n        foreach (var url in urls)\n        {\n            queue.Enqueue(url);\n        }\n\n        // Consumers : traitement des tâches\n        var tasks = new Task[3];\n        for (int i = 0; i < tasks.Length; i++)\n        {\n            tasks[i] = Task.Run(() =>\n            {\n                while (queue.TryDequeue(out var url))\n                {\n                    Console.WriteLine($"Traitement : {url}");\n                    // Simulation de travail\n                    Task.Delay(500).Wait();\n                }\n            });\n        }\n\n        await Task.WhenAll(tasks);\n        Console.WriteLine("Toutes les tâches sont terminées.");\n    }\n}
\n

Avantages : simplicité, hautes performances pour un grand nombre d'opérations.
\nInconvénients : pas de mécanisme d'attente intégré (nécessite une boucle de vérification), difficile de limiter la taille de la file.

\n\n

2. BlockingCollection — file d'attente avec blocage et limite

\n

BlockingCollection<T> est une enveloppe autour d'une collection thread-safe qui ajoute la possibilité de bloquer le thread consommateur lorsque la file est vide, et le thread producteur lorsque la file est pleine. Par défaut, ConcurrentQueue est utilisée, mais on peut substituer n'importe quelle collection implémentant IProducerConsumerCollection<T>.

\n\n

Exemple : traitement de commandes avec limitation du tampon

\n

Imaginez un système de traitement de commandes. Le producteur génère des commandes, et trois consommateurs les traitent. Le tampon est limité à 10 éléments pour ne pas consommer trop de mémoire.

\n
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class BlockingCollectionExample\n{\n    public static async Task RunAsync()\n    {\n        using var orders = new BlockingCollection<string>(boundedCapacity: 10);\n\n        // Producer\n        var producer = Task.Run(() =>\n        {\n            for (int i = 1; i <= 20; i++)\n                

Blogs

Book Recommendations