Implementing a Simple In-Memory Message Queue Using Channels in NET

Listen to this Post

Channels in .NET provide a powerful way to implement the producer-consumer pattern, enabling asynchronous communication between components. Below is a detailed guide on how to use Channels to create a simple in-memory message bus.

How to Use Channels in .NET

Channels consist of a `Writer` for publishing messages and a `Reader` for consuming them. Here’s a basic implementation:

using System.Threading.Channels;

// Create an unbounded channel (can also use bounded for limited capacity)
var channel = Channel.CreateUnbounded<string>();

// Producer: Write messages to the channel
async Task ProduceMessages()
{
for (int i = 0; i < 10; i++)
{
await channel.Writer.WriteAsync($"Message {i}");
Console.WriteLine($"Produced: Message {i}");
await Task.Delay(500);
}
channel.Writer.Complete(); // Signal no more messages
}

// Consumer: Read messages from the channel
async Task ConsumeMessages()
{
await foreach (var message in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Consumed: {message}");
}
}

// Run producer and consumer concurrently
var producer = ProduceMessages();
var consumer = ConsumeMessages();

await Task.WhenAll(producer, consumer);

You Should Know:

  • Bounded vs. Unbounded Channels:
  • Unbounded (CreateUnbounded<T>()) allows unlimited messages.
  • Bounded (CreateBounded<T>(capacity)) restricts queue size, useful for backpressure.

  • Error Handling:

    try
    {
    await channel.Writer.WriteAsync("Test");
    }
    catch (ChannelClosedException)
    {
    Console.WriteLine("Channel closed!");
    }
    

  • Multiple Consumers:

    var channel = Channel.CreateUnbounded<string>();
    var consumer1 = ConsumeMessages("Consumer 1");
    var consumer2 = ConsumeMessages("Consumer 2");
    await Task.WhenAll(consumer1, consumer2);
    

  • Performance Considerations:

  • Channels are lock-free and optimized for high-throughput scenarios.
  • For distributed systems, consider Azure Service Bus or RabbitMQ.

What Undercode Say:

Channels in .NET offer a lightweight alternative to MediatR for in-memory messaging, but they lack built-in retries, persistence, or distributed capabilities. For mission-critical systems, combine Channels with:
– Redis Pub/Sub (redis-cli SUBSCRIBE channel)
– Kafka (kafka-console-producer --topic test)
– Linux IPC (mkfifo /tmp/myfifo)
– Windows Named Pipes (New-Pipe -Name MyPipe)

For .NET developers, mastering Channels improves scalability, but always evaluate if an in-memory solution fits your reliability needs.

Expected Output:

A functional .NET Channel-based message queue with producer-consumer logging.

Reference:

References:

Reported By: Milan Jovanovic – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

Join Our Cyber World:

💬 Whatsapp | 💬 TelegramFeatured Image