Course contentsShow
← C#
Lesson 1542 of 1,90814. Background Work and MessagingFree lesson

Backpressure Management

Implement flow control mechanisms to prevent consumers from being overwhelmed by bursty producers.

In this lesson, you will learn how to implement backpressure to protect your background consumers from being overwhelmed by bursty message producers.

The Problem: When Producers Outpace Consumers

In a high-throughput system, a producer might push data into your Channel<T> or queue faster than your BackgroundService can process it. Without backpressure, your memory usage will climb indefinitely as the queue grows, eventually leading to an OutOfMemoryException. Backpressure is the mechanism of telling the producer to "slow down" when the system reaches its capacity.

Implementing Backpressure with System.Threading.Channels

The most effective way to implement backpressure in C# is by using a Bounded Channel. When you set a BoundedChannelOptions capacity, the WriteAsync method on the channel becomes asynchronous and awaits if the channel is full. This naturally propagates the pressure back to the producer.

// Configure a channel with a limit of 100 items
var channel = Channel.CreateBounded<WorkItem>(new BoundedChannelOptions(100)
{
    FullMode = BoundedChannelFullMode.Wait // The producer will pause here!
});

// In your Producer
async Task Produce(WorkItem item)
{
    // If the queue has 100 items, this await will not complete
    // until the consumer processes an item and frees up a slot.
    await channel.Writer.WriteAsync(item);
}

// In your BackgroundService Consumer
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    await foreach (var item in channel.Reader.ReadAllAsync(stoppingToken))
    {
        await ProcessWorkAsync(item);
    }
}

Choosing a Strategy

When the channel reaches its limit, you have a few choices for BoundedChannelFullMode:

  1. Wait: The producer suspends execution (the standard backpressure approach).
  2. DropNewest/Oldest: Useful if your application prioritizes low latency over processing every single message.
  3. DropWrite: Silently discards the current message.

By choosing Wait, you ensure the system remains stable because the speed of the system is automatically throttled to the speed of the slowest component in your pipeline.

Key Takeaway: Backpressure is achieved by using bounded buffers that force producers to await when the consumer’s capacity is reached, preventing memory exhaustion and ensuring system stability under load.