Queue & Dequeue with Concurrent Dictionary in .NET Core

Introduction

In multi-threaded applications, efficient handling of concurrent data structures is crucial to ensure thread safety and performance. While .NET Core provides a variety of thread-safe collections, each with its own strengths and use cases, there are scenarios where we might need to implement queue and dequeue functionality with a ConcurrentDictionary due to specific requirements or constraints. In this article, we'll explore such scenarios and demonstrate how to implement queue and dequeue operations using ConcurrentDictionary in .NET Core, along with real-time examples.

When to Use Queue and Dequeue with Concurrent Dictionary

Before diving into the implementation, let's understand the scenarios where implementing queue and dequeue functionality with a ConcurrentDictionary is beneficial.

  1. Integration with existing codebase: If you have an existing codebase that heavily relies on ConcurrentDictionary for other purposes, such as key-value pair storage or concurrent access, implementing queue and dequeue functionality with the same data structure can lead to better code organization and maintainability.
  2. Limited external dependencies: In scenarios where introducing additional dependencies or libraries is not feasible or desired, leveraging the existing ConcurrentDictionary class to implement queue-like behavior can be a pragmatic approach.
  3. Custom queuing logic: If you need to implement custom queuing logic or require specific behavior not provided by standard queue implementations, using ConcurrentDictionary allows you to tailor the queuing mechanism to your requirements.
  4. Real-time example: Task Processing Queue Let's consider a real-time example of a task processing system where multiple tasks need to be processed concurrently by worker threads. Each task is represented by an ID and some associated data. We'll implement a queue and dequeue mechanism using a ConcurrentDictionary<int, TaskData> to manage the tasks efficiently.

Implementation

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
class Program
{
    static async Task Main(string[] args)
    {
        // Concurrent dictionary to store tasks
        ConcurrentDictionary<int, TaskData> taskQueue = new ConcurrentDictionary<int, TaskData>();
        // Enqueue tasks
        EnqueueTasks(taskQueue);

        // Dequeue and process tasks
        await ProcessTasksAsync(taskQueue);
    }
    static void EnqueueTasks(ConcurrentDictionary<int, TaskData> taskQueue)
    {
        // Simulate enqueueing tasks
        for (int i = 1; i <= 5; i++)
        {
            taskQueue.TryAdd(i, new TaskData { ID = i, Description = $"Task {i}" });
            Console.WriteLine($"In-queued task: {i}");
        }
    }
    static async Task ProcessTasksAsync(ConcurrentDictionary<int, TaskData> taskQueue)
    {
        while (!taskQueue.IsEmpty)
        {
            // Dequeue and process tasks asynchronously
            foreach (var kvp in taskQueue)
            {
                if (taskQueue.TryRemove(kvp.Key, out TaskData task))
                {
                    Console.WriteLine($"Processing task: {task.Description}");
                    await Task.Delay(1000); // Simulate task processing
                    Console.WriteLine($"Dequeue task: {task.Description}");
                }
            }
        }
    }
}
class TaskData
{
    public int ID { get; set; }
    public string Description { get; set; }
}

Explanation output

  • The output remains similar to the previous explanation, with tasks being enqueued, dequeued, and processed in the same order as before.
  • However, the message "Completed task" has been replaced with "Dequeued task" to accurately reflect the operation of removing tasks from the queue, aligning with the terminology used in queue-based data structures
    Queue based data

Conclusion

In this article, we explored the scenarios where implementing queue and dequeue functionality with a ConcurrentDictionary is beneficial, along with a real-time example of a task processing system. By leveraging the inherent thread safety and concurrency support provided by ConcurrentDictionary, we can implement efficient and scalable queue and dequeue operations tailored to specific requirements and constraints in .NET Core applications. However, it's essential to carefully consider the trade-offs and suitability of this approach in the context of your application's architecture and performance requirements.

😊Please consider liking and following me for more articles and if you find this content helpful.👍


Similar Articles
Citiustech Healthcare Technology Pvt Ltd
CitiusTech plays a deep and meaningful role in powering the future of healthcare.