0Pricing
C# Academy · Lektion

Producer/Consumer mit Channels (Überblick)

Modellieren Sie Producer-/Consumer-Warteschlangen wie Channels mit BlockingCollection : begrenzte Puffer (Backpressure), mehrere Consumer und ein ordnungsgemäßer Abschluss.

Producer/Consumer mit Channels (Überblick) ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 3. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 3 Lektionen.

Konzept und Ziele

Ziel: Erstellen Sie eine sichere Produzenten-/Konsumentenpipeline.

  • Channel-Idee: eine Warteschlange zwischen Produzenten und Konsumenten
  • Begrenzte Kapazität sorgt für Backpressure
  • Unterstützung für mehrere Konsumenten
  • Verwenden Sie CompleteAdding für ein sauberes Herunterfahren

Ein Produzent/ein Konsument

Grundablauf: Produzenten rufen Add auf, Konsumenten durchlaufen GetConsumingEnumerable; anschließend rufen Sie CompleteAdding zum Beenden auf.

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);
  }
}

Backpressure-Demo

Begrenzte Warteschlangen erzeugen Backpressure: Wenn sie voll sind, blockiert Add, bis Konsumenten Platz schaffen.

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);
  }
}

Konsumenten mit Fan-out

Mehrere Konsumenten konkurrieren um Elemente (Fan-out). Die Arbeit wird automatisch auf Threads verteilt.

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());
  }
}

Sauberer Abschluss

Verwenden Sie CompleteAdding und GetConsumingEnumerable, um die Verarbeitung sauber zu beenden, sobald die Warteschlange leer ist.

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);
  }
}

Tipps und Fallstricke

Tipps:

  • Bevorzugen Sie begrenzte Puffer, um Speicherwachstum zu verhindern.
  • Verwenden Sie GetConsumingEnumerable, um Race Conditions beim Abschluss zu vermeiden.
  • Halten Sie Arbeitselemente klein und unabhängig; vermeiden Sie gemeinsam genutzten Zustand.
  • Protokollieren und behandeln Sie Fehler innerhalb der Konsumenten, um unbemerkte Abbrüche zu vermeiden.

Zweck von CompleteAdding

Schnelltest: Was bewirkt der Aufruf von CompleteAdding() für eine BlockingCollection?

Zusammenfassung

Zusammenfassung: Verwenden Sie eine begrenzte BlockingCollection, um Kanäle nachzubilden, verteilen Sie die Arbeit auf mehrere Konsumenten und verwenden Sie CompleteAdding für ein geordnetes Herunterfahren.

Häufig gestellte Fragen

Ist die Lektion „Producer/Consumer mit Channels (Überblick)“ kostenlos?

Ja — der vollständige Text von „Producer/Consumer mit Channels (Überblick)“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 3 Lektionen.

Was lerne ich in „Producer/Consumer mit Channels (Überblick)“?

Modellieren Sie Producer-/Consumer-Warteschlangen wie Channels mit BlockingCollection : begrenzte Puffer (Backpressure), mehrere Consumer und ein ordnungsgemäßer Abschluss. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 3.

Wie lange dauert die Lektion „Producer/Consumer mit Channels (Überblick)“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Parallel.ForEach, PLINQ
  2. Producer/Consumer mit Channels (Überblick)
  3. Abwägung zwischen Durchsatz und Latenz
← Zurück zu C# Academy