Retour à l'acceuil

Information sur l'article

Categorie: csharp, channels, concurrency, async, pipelines

Mise à jour: 2025-08-09

Temps de lecture: 9 min

C# Channels : construire un pipeline async clair et efficace

C# Channels : construire un pipeline async clair et efficace

Mise à jour: 2025-08-09


Pourquoi utiliser les Channels ?


System.Threading.Channels fournit une file asynchrone hautes performances pour relier des producteurs et des consommateurs avec backpressure intégré.


Exemple concret : traiter des commandes en parallèle


Objectif: ingérer des commandes, les traiter en parallèle (limité), et s’arrêter propreement (annulation).


Points clés


- BoundedChannel = backpressure: si plein, WriteAsync attend. Idéal pour protéger une ressource (DB, API).

- ReadAllAsync(ct) = boucle asynchrone propre qui s’arrête quand le writer est Completed (et le canal vidé).

- SingleWriter/SingleReader optimisent les verrous internes si c’est votre cas.

- writer.Complete() signale la fin; tous les consommateurs finissent naturellement.


Variante : pipeline en 2 étapes (validation -> persist)



Bonnes pratiques


- Toujours consommer via ReadAllAsync dans un while/await foreach (et non TryRead en boucle occupée).

- Annulation: passez un CancellationToken à ReadAllAsync/WriteAsync; ne pas oublier Complete() côté writer.

- Dimensionner la capacité selon la latence des consommateurs (pointe × latence), et mesurer.

- Préférer Channels à BlockingCollection en contexte async; évitez les locks inutiles.


Comparaison : Channels vs Parallel.ForEach vs ConcurrentQueue


- Channels: parfaits pour des flux asynchrones (producteur continu), backpressure natif, arrêt propre via Complete(), "await foreach" lisible.

- Parallel.ForEach/ForEachAsync: pratique pour traiter un ensemble fini connu d’avance, limiter le parallélisme CPU, mais pas de sémantique de file ni de backpressure dynamique.

- ConcurrentQueue: thread-safe mais pas de backpressure ni de "signal" intégré; il faut ajouter sémaphores/événements et gérer l’arrêt/annulation manuellement.


Exemple Parallel.ForEachAsync (sans backpressure)



Exemple ConcurrentQueue + SemaphoreSlim (gestion manuelle)



Quand choisir quoi ?


- Flux continu, besoin de backpressure et d’un arrêt propre: Channels.

- Batch fini connu d’avance, parallélisme simple: Parallel.ForEach/ForEachAsync.

- Interop ou héritage existant avec queues: ConcurrentQueue (+ sémaphores/événements) mais privilégier Channels en nouveau code async.


Conclusion


Merci d’avoir lu cet article. Si vous avez des questions, envie d’améliorer l’exemple ou de suggérer un sujet, contactez-moi: je serai ravi d’échanger.