Introduction
The ConcurrentQueue<T>
class in C# is part of the System.Collections.Concurrent
namespace and represents a thread-safe, first-in, first-out (FIFO) collection of objects. It is particularly useful in scenarios where multiple threads are adding and removing items from a collection simultaneously, and you need a queue-like behavior. The ConcurrentQueue<T>
is designed to be thread-safe and optimized for high concurrency.
Key Features of ConcurrentQueue
- Thread-Safe: Supports concurrent enqueue and dequeue operations.
- FIFO Order: Maintains elements in a first-in, first-out order.
- High Throughput: Optimized for scenarios with high contention.
- No Locking: Uses lightweight synchronization mechanisms to avoid locks.
Creating a ConcurrentQueue
Declaration and Initialization
You can declare and initialize a ConcurrentQueue<T>
in several ways:
Example
using System;
using System.Collections.Concurrent;
namespace ConcurrentQueueExample
{
class Program
{
static void Main(string[] args)
{
// Creating an empty ConcurrentQueue
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
// Adding elements to the ConcurrentQueue
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
// Displaying elements
Console.WriteLine("Elements in ConcurrentQueue:");
foreach (int item in queue)
{
Console.WriteLine(item);
}
}
}
}
Output
Elements in ConcurrentQueue:
1
2
3
Common Operations on ConcurrentQueue
Adding Elements
- Enqueue: Adds an element to the end of the
ConcurrentQueue
.
queue.Enqueue(4);
Removing Elements
- TryDequeue: Attempts to remove and return the object at the beginning of the
ConcurrentQueue
. Returnstrue
if an object was removed successfully; otherwise,false
.
bool success = queue.TryDequeue(out int item);
if (success)
{
Console.WriteLine($"Dequeued: {item}");
}
else
{
Console.WriteLine("No items to dequeue.");
}
Peeking at Elements
- TryPeek: Attempts to return the object at the beginning of the
ConcurrentQueue
without removing it. Returnstrue
if an object was returned successfully; otherwise,false
.
bool success = queue.TryPeek(out int item);
if (success)
{
Console.WriteLine($"Peeked: {item}");
}
else
{
Console.WriteLine("No items to peek.");
}
Checking the Collection Size
- Count: Gets the number of elements in the
ConcurrentQueue
.
int count = queue.Count;
Console.WriteLine($"Count: {count}");
Iterating Through a ConcurrentQueue
You can iterate through a ConcurrentQueue
using a foreach
loop. However, note that the collection is thread-safe for concurrent modifications, and the snapshot taken by foreach
might not reflect all concurrent changes.
Example
foreach (int item in queue)
{
Console.WriteLine(item);
}
Practical Example
Let’s create a practical example where we use a ConcurrentQueue<T>
to manage a collection of tasks executed by multiple threads, allowing them to add completed tasks to the queue.
Example
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace TaskCompletionExample
{
class Program
{
static void Main(string[] args)
{
ConcurrentQueue<string> completedTasks = new ConcurrentQueue<string>();
// Creating and starting multiple tasks
Task[] tasks = new Task[5];
for (int i = 0; i < tasks.Length; i++)
{
int taskId = i;
tasks[i] = Task.Run(() => CompleteTask(completedTasks, taskId));
}
// Waiting for all tasks to complete
Task.WaitAll(tasks);
// Displaying completed tasks
Console.WriteLine("Completed Tasks:");
foreach (string task in completedTasks)
{
Console.WriteLine(task);
}
}
static void CompleteTask(ConcurrentQueue<string> completedTasks, int taskId)
{
// Simulate some work
Task.Delay(1000).Wait();
string taskName = $"Task {taskId}";
completedTasks.Enqueue(taskName);
Console.WriteLine($"{taskName} completed.");
}
}
}
Output
Task 0 completed.
Task 1 completed.
Task 2 completed.
Task 3 completed.
Task 4 completed.
Completed Tasks:
Task 0
Task 1
Task 2
Task 3
Task 4
Conclusion
The ConcurrentQueue<T>
class in C# provides a thread-safe, first-in, first-out (FIFO) collection of objects that is optimized for high concurrency. It supports concurrent enqueue and dequeue operations without locking, making it ideal for scenarios where multiple threads need to interact with a shared queue. Understanding how to use ConcurrentQueue<T>
effectively can help you manage collections of data in multi-threaded applications.