Course contentsShow
← C#
Lesson 1465 of 1,90813. Real-time Communication with SignalRFree lesson

Shared Memory Communication Channels

Use Channels or ConcurrentQueues to decouple message ingestion from broadcast delivery.

Decoupling Ingestion from Broadcast with Channels

In this lesson, you will learn how to decouple incoming data ingestion from real-time SignalR broadcasts using high-performance System.Threading.Channels to prevent blocking your ingestion threads.

The Problem: Blocking the Hub

When your SignalR application receives high-frequency data—such as sensor telemetry or live stock updates—performing a broadcast directly inside the ingestion method can lead to contention. If the IHubContext broadcast operation takes longer than the data arrival rate, your ingestion logic stalls. This creates a "tight coupling" where the speed of your data source is limited by the speed of your client delivery network.

The Solution: Buffered Channels

By introducing a Channel<T>, you create an in-memory buffer that acts as a shock absorber. Your ingestion logic simply writes to the Channel, returning immediately. A background worker (a BackgroundService) then reads from the Channel and performs the IHubContext broadcast.

Consider this implementation pattern:

public class TelemetryBackgroundService : BackgroundService
{
    private readonly Channel<TelemetryData> _channel;
    private readonly IHubContext<TelemetryHub> _hubContext;

    public TelemetryBackgroundService(Channel<TelemetryData> channel, IHubContext<TelemetryHub> hubContext)
    {
        _channel = channel;
        _hubContext = hubContext;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Read items from the channel as they arrive
        await foreach (var data in _channel.Reader.ReadAllAsync(stoppingToken))
        {
            await _hubContext.Clients.All.SendAsync("ReceiveUpdate", data, stoppingToken);
        }
    }
}

Why this is Advanced

Using Channel<T>.CreateBounded(capacity) allows you to enforce backpressure. If the consumer cannot keep up with the producer, you can define strategies—like BoundedChannelFullMode.DropOldest—to ensure your application stays responsive even under extreme load. This pattern keeps your Hub methods "lean," ensuring they only focus on message arrival, while the background delivery pipeline manages the heavy lifting of distribution across the SignalR connection pool.

Key Takeaway: Use System.Threading.Channels as an asynchronous buffer to separate the rapid ingestion of data from the I/O-bound task of broadcasting messages to connected SignalR clients.