Task Queues for C# (.NET): From Basic Primitives to Industrial Solutions
Organizing asynchronous task processing is one of the key challenges when developing high-load applications in C#. The .NET ecosystem offers several approaches: from built-in synchronization primitives to powerful external libraries. In this article, we will review the main tools for working with task queues, their strengths, and usage examples.
1. System.Threading.Channels — Built-in .NET Tool
Starting with .NET Core 2.1, the standard library introduced the System.Threading.Channels.Channel<T> class. This is a thread-safe queue optimized for producer-consumer scenarios.
Key Features:
- Asynchronous reading and writing (
WriteAsync,ReadAsync) - Support for bounded and unbounded queues
- Ability to complete the queue to signal consumers
- High performance with lock-free implementation
Usage Example:
using System.Threading.Channels;
var channel = Channel.CreateBounded<string>(100);
// Producervar producer = Task.Run(async () =>{ for (int i = 0; i < 10; i++) { await channel.Writer.WriteAsync($"Task {i}"); await Task.Delay(100); } channel.Writer.Complete();});
// Consumervar consumer = Task.Run(async () =>{ await foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine($"Processing: {item}"); await Task.Delay(200); }});
await Task.WhenAll(producer, consumer);When to use: for internal queues within a single process when persistence or distributed processing is not required.
2. Hangfire — Advanced Background Job Library
Hangfire is one of the most popular libraries for managing background jobs in .NET. It supports queues, scheduling, retries, and monitoring.
Key Features:
- Persistence (SQL Server, Redis, PostgreSQL, etc.)
- Web interface for monitoring
- Support for delayed and recurring jobs
- Automatic retries on failures
Setup and Usage Example:
// Installation: Install-Package Hangfire// In Startup.cspublic void ConfigureServices(IServiceCollection services){ services.AddHangfire(config => config.UseSqlServerStorage(connectionString)); services.AddHangfireServer();}
// Usagepublic class EmailService{ public void SendWelcomeEmail(int userId) { // Email sending logic Console.WriteLine($"Sending email to user {userId}"); }}
// Enqueue a jobBackgroundJob.Enqueue<EmailService>(x => x.SendWelcomeEmail(42));
// Delayed jobBackgroundJob.Schedule<EmailService>( x => x.SendWelcomeEmail(43), TimeSpan.FromMinutes(5));When to use: for web applications where job persistence across restarts, monitoring, and flexible scheduling are required.
3. RabbitMQ.Client — Distributed Message Queue
For distributed systems where multiple services need to exchange tasks, RabbitMQ is used. The RabbitMQ.Client library provides full access to the AMQP protocol.
Basic Producer-Consumer Example:
using RabbitMQ.Client;using System.Text;
// Producervar factory = new ConnectionFactory() { HostName = "localhost" };using var connection = factory.CreateConnection();using var channel = connection.CreateModel();
channel.QueueDeclare(queue: "task_queue", durable: true, exclusive