Introduction: Why Do You Need Task Queues in C#?
Imagine you are developing an application that processes orders in an online store. Clients send requests simultaneously, and each request takes time (for example, payment verification or sending an email). If you process them sequentially, the application will slow down, and users will wait. The solution is to use a task queue.
A task queue is a data structure that works on the FIFO principle (First In, First Out). In C# (.NET), queues are implemented in several variations: from the simple Queue<T> to the thread-safe ConcurrentQueue<T>. In this article, we will cover how to create queues, add and retrieve tasks, and how to use them in asynchronous programming.
This article is aimed at beginners, so we will start with the basics and gradually move to more complex examples.
1. Basics of Working with Queue<T> in C#
The Queue<T> class is located in the System.Collections.Generic namespace. This is a simple queue that does not support multithreading — it should only be used in single-threaded scenarios.
Creating a Queue and Basic Operations
Let's create a queue to store string tasks:
using System;using System.Collections.Generic;
class Program{ static void Main() { // Create a queue of strings Queue<string> taskQueue = new Queue<string>();
// Add tasks (Enqueue) taskQueue.Enqueue("Send email to client"); taskQueue.Enqueue("Check order status"); taskQueue.Enqueue("Update database");
Console.WriteLine($"Number of tasks: {taskQueue.Count}");
// Retrieve a task (Dequeue) — removes and returns the first element string firstTask = taskQueue.Dequeue(); Console.WriteLine($"Executing: {firstTask}");
// View the next task without removing it (Peek) string nextTask = taskQueue.Peek(); Console.WriteLine($"Next: {nextTask}");
Console.WriteLine($"Remaining tasks: {taskQueue.Count}"); }}Program output:
Number of tasks: 3Executing: Send email to clientNext: Check order statusRemaining tasks: 2Important Queue<T> Methods
Enqueue(item)— adds an element to the end of the queue.Dequeue()— removes and returns the first element. If the queue is empty, it throws anInvalidOperationException.Peek()— returns the first element without removing it.Clear()— clears the queue.Contains(item)— checks if an element exists in the queue.
Important: Queue<T> is not thread-safe. If multiple threads add or retrieve elements simultaneously, errors will occur. For multithreaded scenarios, use ConcurrentQueue<T>.
2. Thread-Safe Queue: ConcurrentQueue<T>
In real applications, tasks are often added and processed in different threads. For this, .NET provides ConcurrentQueue<T> from the System.Collections.Concurrent namespace.
Example: Producer-Consumer
Imagine one thread (Producer) generates tasks, and another (Consumer) executes them.
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
class Program{ static void Main() { ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
// Producer: adds tasks