Files d'attente de tâches en C# (.NET) : System.Threading.Channels et TPL Dataflow

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

Files d'attente de tâches pour C# (.NET) : System.Threading.Channels et TPL Dataflow

Lors du développement d'applications multithread en C# (.NET), il est souvent nécessaire d'organiser le transfert asynchrone de données entre les producteurs (producers) et les consommateurs (consumers). Le modèle classique Producer-Consumer est résolu à l'aide de files d'attente de tâches. Dans l'écosystème .NET, il existe deux outils puissants pour cela : System.Threading.Channels (apparu dans .NET Core 3.0) et la bibliothèque TPL Dataflow (partie de .NET depuis la version 4.5).

Dans cet article, nous analyserons les deux approches, leurs caractéristiques et fournirons des exemples de code pratiques.



1. System.Threading.Channels — une file d'attente légère et rapide

L'espace de noms System.Threading.Channels fournit des files d'attente asynchrones et thread-safe, optimisées pour les scénarios à haut débit. Les principaux types :

  • Channel<T> — classe abstraite du canal.
  • Channel.CreateUnbounded<T>() — file d'attente illimitée (risque de saturation mémoire).
  • Channel.CreateBounded<T>(int capacity) — file d'attente limitée avec contrôle de pression.
  • ChannelWriter<T> — interface pour l'écriture.
  • ChannelReader<T> — interface pour la lecture.


Exemple : Producer-Consumer avec Channel

Créons un exemple simple où un producteur génère des nombres et un consommateur les traite.

using System.Threading.Channels;using System.Threading.Tasks;

var channel = Channel.CreateUnbounded<int>();

// Producervar producer = Task.Run(async () =>{ for (int i = 0; i < 10; i++) { await channel.Writer.WriteAsync(i); Console.WriteLine($"Produit : {i}"); await Task.Delay(100); } channel.Writer.Complete(); // Signal de fin});

// Consumervar consumer = Task.Run(async () =>{ await foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine($"Traité : {item * 2}"); await Task.Delay(200); }});

await Task.WhenAll(producer, consumer);Console.WriteLine("Terminé !");

Avantages de Channels :

  • Frais généraux minimaux (sans allocation d'objets supplémentaires).
  • Prise en charge intégrée de async/await et IAsyncEnumerable.
  • Idéal pour les systèmes à forte charge (par exemple, traitement de paquets réseau).


2. TPL Dataflow — un pipeline de traitement puissant

La bibliothèque System.Threading.Tasks.Dataflow fournit des blocs plus complexes pour construire des pipelines et des réseaux de traitement de données. Les principaux blocs :

  • BufferBlock<T> — une file d'attente simple.
  • ActionBlock<T> — bloc exécutant une action sur chaque élément.
  • TransformBlock<TInput, TOutput> — transforme chaque élément.
  • BatchBlock<T> — regroupe les éléments en lots.


Exemple : Pipeline avec ActionBlock

Réalisons la même tâche : le producteur envoie des nombres, le consommateur les traite.

using System.Threading.Tasks.Dataflow;

var actionBlock = new ActionBlock<int>(async item =>{ await Task.Delay(200); Console.WriteLine($"Traité : {item * 2}");}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });

// Producerfor (int i = 0; i < 10; i++){ await actionBlock.SendAsync(i); Console.WriteLine($"Produit : {i}"); await Task.Delay(100);}

actionBlock.Complete();await actionBlock.Completion;Console.WriteLine("Terminé !");

Avantages de TPL Dataflow :

  • Configuration flexible

Recommendations