Canale Asincrone de Mare Viteză în .NET: System.Threading.Channels și Modelul Producător-Consumator în C# (.NET)

Ghid complet pentru System.Threading.Channels în .NET: implementarea modelului producător-consumator de înaltă frecvență, canale limitate (Bounded) cu backpressure și optimizări SingleWriter/SingleReader fără blocaje.

Sintaxă C#
Channel<T> channel = Channel.CreateBounded<T>(1000); | channel.Writer.WriteAsync()

În multe arhitecturi de backend, anumite componente generează date într-un ritm mult mai rapid decât pot fi acestea procesate și salvate pe disc sau transmise pe rețea. Folosirea unor colecții tradiționale precum BlockingCollection<T> sau cozi cu lock blochează firele de execuție și degradează masiv performanța.

Namespace-ul System.Threading.Channels oferă o implementare modernă, complet asincronă și de ultra-înaltă performanță a cozilor concurente producător-consumator, inspirată din canalele native din limbajul Go.

Implementarea unui Canal Delimitat (Bounded Channel) cu Backpressure
using System.Threading.Channels;

public class ServiciuProcesareEvenimente
{
    // Creăm un canal delimitat cu capacitate de 100 de elemente:
    private readonly Channel<string> _canal = Channel.CreateBounded<string>(new BoundedChannelOptions(100)
    {
        // Când coada este plină, producătorul așteaptă asincron fără să consume CPU (Backpressure):
        FullMode = BoundedChannelFullMode.Wait,
        SingleReader = true, // Optimizare internă fără lock dacă există un singur consumator
        SingleWriter = false // Mai mulți producători concurenți
    });

    // 1. Producătorul publică mesaje:
    public async ValueTask PublicaEvenimentAsync(string mesaj)
    {
        await _canal.Writer.WriteAsync(mesaj);
    }

    // 2. Consumatorul procesează fluxul asincron continuu:
    public async Task PornesteConsumatorAsync(CancellationToken ct)
    {
        // ReadAllAsync returnează un IAsyncEnumerable ce se consumă elegant cu await foreach:
        await foreach (string mesaj in _canal.Reader.ReadAllAsync(ct))
        {
            await ProceseazaMesajAsync(mesaj);
        }
    }

    private async Task ProceseazaMesajAsync(string m)
    {
        await Task.Delay(10); // Simulăm persistența sau apelul extern
        Console.WriteLine($"Mesaj procesat din canal: {m}");
    }
}

Politici de Comportament la Saturarea Canalului (FullMode)

  • Wait (Recomandat): Producătorul este suspendat asincron până când consumatorul eliberează un slot în coadă.
  • DropOldest: Elimină cel mai vechi mesaj din canal pentru a face loc noului mesaj (ideal pentru metrici în timp real).
  • DropWrite: Renunță silențios la mesajul nou dacă coada este plină.