0Pricing
C# Academy · Leçon

Producteur/consommateur avec Channels (vue d’ensemble)

Modélisez des files producteur/consommateur comme Channels avec BlockingCollection : tampons de taille limitée (contre-pression), plusieurs consommateurs et achèvement progressif.

Producteur/consommateur avec Channels (vue d’ensemble) est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 3. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 3 leçons au total.

Concepts et objectifs

Objectif : créer une chaîne de traitement producteur-consommateur sûre.

  • Idée du canal : une file d’attente entre les producteurs et les consommateurs
  • Une capacité bornée fournit une contre-pression
  • Prendre en charge plusieurs consommateurs
  • Utiliser CompleteAdding pour un arrêt propre

Un producteur et un consommateur

Flux de base : les producteurs utilisent Add, les consommateurs parcourent GetConsumingEnumerable, puis CompleteAdding termine le traitement.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  public static void Main(string[] args)
  {
    // Unbounded by default; simple demo
    BlockingCollection<int> queue = new BlockingCollection<int>();

    // Producer
    Task producer = Task.Run(() =>
    {
      for (int i = 1; i <= 5; i++)
      {
        queue.Add(i);                 // enqueue
        Console.WriteLine("Produced " + i);
        Thread.Sleep(50);             // simulate work
      }
      queue.CompleteAdding();         // signal no more items
    });

    // Consumer
    Task consumer = Task.Run(() =>
    {
      foreach (int x in queue.GetConsumingEnumerable())
      {
        Console.WriteLine("Consumed " + x);
        Thread.Sleep(80);             // simulate processing
      }
      Console.WriteLine("Consumer done");
    });

    Task.WaitAll(producer, consumer);
  }
}

Démonstration de la contre-pression

Les files d’attente bornées appliquent une contre-pression : lorsqu’elles sont pleines, Add se bloque jusqu’à ce que les consommateurs libèrent de l’espace.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  public static void Main(string[] args)
  {
    // Bounded capacity introduces backpressure
    BlockingCollection<int> queue = new BlockingCollection<int>(boundedCapacity: 2);

    Task producer = Task.Run(() =>
    {
      for (int i = 1; i <= 5; i++)
      {
        queue.Add(i); // blocks when the buffer is full
        Console.WriteLine("Produced " + i + " (count=" + queue.Count + ")");
      }
      queue.CompleteAdding();
    });

    Task consumer = Task.Run(() =>
    {
      foreach (int x in queue.GetConsumingEnumerable())
      {
        Console.WriteLine("Consumed " + x);
        Thread.Sleep(120); // slower consumer -> producer will block sometimes
      }
    });

    Task.WaitAll(producer, consumer);
  }
}

Distribution entre consommateurs

Plusieurs consommateurs se disputent les éléments (distribution). Le travail est automatiquement réparti entre les threads.

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  public static void Main(string[] args)
  {
    BlockingCollection<int> queue = new BlockingCollection<int>(3);

    Task prod = Task.Run(() =>
    {
      for (int i = 1; i <= 8; i++)
      {
        queue.Add(i);
        Console.WriteLine("Produced " + i);
        Thread.Sleep(30);
      }
      queue.CompleteAdding();
    });

    // Start 2 consumers that compete for items
    Task[] consumers = Enumerable.Range(1, 2).Select(id => Task.Run(() =>
    {
      foreach (int x in queue.GetConsumingEnumerable())
      {
        Console.WriteLine("C" + id + " got " + x);
        Thread.Sleep(100);
      }
      Console.WriteLine("C" + id + " done");
    })).ToArray();

    Task.WaitAll(consumers.Concat(new[] { prod }).ToArray());
  }
}

Achèvement propre

Utilisez CompleteAdding et GetConsumingEnumerable pour terminer proprement lorsque la file d’attente est vide.

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
  static void Producer(BlockingCollection<string> q)
  {
    string[] lines = { "A", "B", "C", "D" };
    foreach (var s in lines)
    {
      q.Add(s);
      Console.WriteLine("Produced " + s);
    }
    q.CompleteAdding(); // signal completion
  }

  static void Consumer(BlockingCollection<string> q)
  {
    try
    {
      foreach (var item in q.GetConsumingEnumerable())
      {
        Console.WriteLine("Consumed " + item);
        Thread.Sleep(60);
      }
      Console.WriteLine("Consumer finished all items");
    }
    catch (InvalidOperationException)
    {
      // Thrown if taken after completion and empty; avoided by foreach above.
    }
  }

  public static void Main(string[] args)
  {
    var queue = new BlockingCollection<string>(2);
    Task p = Task.Run(() => Producer(queue));
    Task c = Task.Run(() => Consumer(queue));
    Task.WaitAll(p, c);
  }
}

Conseils et pièges

Conseils :

  • Préférez les tampons bornés pour empêcher la croissance de la mémoire.
  • Utilisez GetConsumingEnumerable pour éviter les problèmes de concurrence lors de l’achèvement.
  • Gardez les éléments de travail petits et indépendants ; évitez l’état partagé.
  • Journalisez et gérez les erreurs dans les consommateurs pour éviter les arrêts silencieux.

Rôle de CompleteAdding

Vérification rapide : que fait l’appel de CompleteAdding() sur une BlockingCollection ?

Récapitulatif

Récapitulatif : utilisez une BlockingCollection bornée pour simuler des canaux, répartissez le travail entre plusieurs consommateurs et utilisez CompleteAdding pour un arrêt progressif.

Questions Fréquemment Posées

La leçon « Producteur/consommateur avec Channels (vue d’ensemble) » est-elle gratuite ?

Oui — le texte complet de « Producteur/consommateur avec Channels (vue d’ensemble) » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 3 leçons au total.

Qu'est-ce que j'apprendrai dans « Producteur/consommateur avec Channels (vue d’ensemble) » ?

Modélisez des files producteur/consommateur comme Channels avec BlockingCollection : tampons de taille limitée (contre-pression), plusieurs consommateurs et achèvement progressif. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer C# Academy ?

Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 3.

Combien de temps prend la leçon « Producteur/consommateur avec Channels (vue d’ensemble) » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?

Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Parallel.ForEach et PLINQ
  2. Producteur/consommateur avec Channels (vue d’ensemble)
  3. Compromis entre débit et latence
← Retour à C# Academy