ConcurrencyC#verifiedVerified

Producer-Consumer Pattern in C#

Decouple data production from data consumption using a shared buffer, allowing each side to operate at its own pace.

How to Implement the Producer-Consumer Pattern in C#

1Step 1: Define the work item

public record WorkItem(int Id, string Data);

2Step 2: Producer writes items to a channel

public static class Producer
{
    public static async Task ProduceAsync(
        ChannelWriter<WorkItem> writer, int count)
    {
        for (var i = 0; i < count; i++)
        {
            await writer.WriteAsync(new WorkItem(i, $"Item-{i}"));
            Console.WriteLine($"Produced: {i}");
        }
        writer.Complete();
    }
}

3Step 3: Consumer reads items from the channel

public static class Consumer
{
    public static async Task ConsumeAsync(
        ChannelReader<WorkItem> reader, string name)
    {
        await foreach (var item in reader.ReadAllAsync())
        {
            Console.WriteLine($"{name} consumed: {item.Id}");
            await Task.Delay(50); // Simulate processing
        }
    }
}

// Usage:
// var channel = Channel.CreateBounded<WorkItem>(10);
// await Task.WhenAll(
//     Producer.ProduceAsync(channel.Writer, 20),
//     Consumer.ConsumeAsync(channel.Reader, "Worker-1"),
//     Consumer.ConsumeAsync(channel.Reader, "Worker-2"));

Producer-Consumer Pattern Architecture

hourglass_empty

Rendering diagram...

lightbulb

Producer-Consumer Pattern in the Real World

Think of a bakery where bakers (producers) place fresh loaves on a display shelf (the queue) and customers (consumers) pick them up at their leisure. The shelf decouples the baking schedule from customer arrival times — bakers keep baking even when no customer is present, and customers keep shopping even when bakers are on break.