C# ConcurrentDictionary

Introduction

The ConcurrentDictionary<TKey, TValue> class in C# is part of the System.Collections.Concurrent namespace and represents a thread-safe collection of key/value pairs. It is optimized for scenarios where multiple threads are reading from and writing to the dictionary concurrently. ConcurrentDictionary<TKey, TValue> provides high throughput and thread-safe access to its elements without the need for external synchronization.

Key Features of ConcurrentDictionary<TKey, TValue>

  • Thread-Safe: Supports concurrent read and write operations.
  • High Throughput: Optimized for scenarios with high contention.
  • Type Safety: Ensures type safety for keys and values.
  • Lock-Free: Uses fine-grained locking and lock-free techniques to minimize contention.

Creating a ConcurrentDictionary

Declaration and Initialization

You can declare and initialize a ConcurrentDictionary<TKey, TValue> in several ways:

Example

using System;
using System.Collections.Concurrent;

namespace ConcurrentDictionaryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Creating an empty ConcurrentDictionary
            ConcurrentDictionary<int, string> employees = new ConcurrentDictionary<int, string>();

            // Creating a ConcurrentDictionary with initial elements
            var departments = new ConcurrentDictionary<int, string>
            {
                [1] = "Human Resources",
                [2] = "Finance",
                [3] = "Engineering"
            };

            // Adding elements to the ConcurrentDictionary
            employees.TryAdd(101, "John Doe");
            employees.TryAdd(102, "Jane Smith");
            employees.TryAdd(103, "Sam Brown");

            // Displaying elements
            Console.WriteLine("Employees:");
            foreach (var kvp in employees)
            {
                Console.WriteLine($"ID: {kvp.Key}, Name: {kvp.Value}");
            }
        }
    }
}

Output

Employees:
ID: 101, Name: John Doe
ID: 102, Name: Jane Smith
ID: 103, Name: Sam Brown

Common Operations on ConcurrentDictionary

Adding and Updating Elements

  • TryAdd: Adds a key/value pair to the ConcurrentDictionary if the key does not already exist.
bool added = employees.TryAdd(104, "Alice Johnson");
  • AddOrUpdate: Adds a key/value pair if the key does not exist, or updates the value if the key already exists.
string updatedName = employees.AddOrUpdate(101, "John Smith", (key, oldValue) => "John Smith");
  • GetOrAdd: Retrieves the value associated with the specified key, or adds a key/value pair if the key does not exist.
string name = employees.GetOrAdd(105, "Eve White");

Removing Elements

  • TryRemove: Attempts to remove and return the value with the specified key from the ConcurrentDictionary. Returns true if the key was found and removed; otherwise, false.
bool removed = employees.TryRemove(102, out string removedName);

Accessing Elements

  • Indexer: Accesses elements by their key.
string employeeName = employees[101];
  • ContainsKey: Checks if the ConcurrentDictionary contains the specified key. Returns true if the key is found; otherwise, false.
bool hasEmployee = employees.ContainsKey(101); // true

Iterating Through a ConcurrentDictionary

You can iterate through a ConcurrentDictionary using a foreach loop. The collection is thread-safe for concurrent modifications, and the snapshot taken by foreach might not reflect all concurrent changes.

Example

foreach (var kvp in employees)
{
    Console.WriteLine($"ID: {kvp.Key}, Name: {kvp.Value}");
}

Checking the Dictionary Size

  • Count: Gets the number of key/value pairs in the ConcurrentDictionary.
int count = employees.Count;
Console.WriteLine($"Count: {count}");

Practical Example

Let’s create a practical example where we use a ConcurrentDictionary<TKey, TValue> to manage a collection of product inventories, allowing multiple threads to add, update, and remove products concurrently.

Example

using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace ProductInventoryExample
{
    class Program
    {
        static void Main(string[] args)
        {
            ConcurrentDictionary<int, int> productInventory = new ConcurrentDictionary<int, int>();

            // Adding initial products
            productInventory.TryAdd(1, 100);
            productInventory.TryAdd(2, 150);
            productInventory.TryAdd(3, 200);

            // Creating and starting multiple tasks to update inventory
            Task[] tasks = new Task[5];
            for (int i = 0; i < tasks.Length; i++)
            {
                int taskId = i;
                tasks[i] = Task.Run(() => UpdateInventory(productInventory, taskId));
            }

            // Waiting for all tasks to complete
            Task.WaitAll(tasks);

            // Displaying final product inventory
            Console.WriteLine("Product Inventory:");
            foreach (var kvp in productInventory)
            {
                Console.WriteLine($"Product ID: {kvp.Key}, Quantity: {kvp.Value}");
            }
        }

        static void UpdateInventory(ConcurrentDictionary<int, int> inventory, int taskId)
        {
            Random random = new Random();
            for (int i = 0; i < 10; i++)
            {
                int productId = random.Next(1, 4);
                int quantityChange = random.Next(-10, 11);
                inventory.AddOrUpdate(productId, quantityChange, (key, oldValue) => oldValue + quantityChange);
                Console.WriteLine($"Task {taskId} updated product {productId} by {quantityChange}");
                Task.Delay(100).Wait(); // Simulate some work
            }
        }
    }
}

Output

Task 0 updated product 3 by 5
Task 1 updated product 2 by -7
Task 2 updated product 1 by 3
...
Product Inventory:
Product ID: 1, Quantity: 97
Product ID: 2, Quantity: 145
Product ID: 3, Quantity: 210

Conclusion

The ConcurrentDictionary<TKey, TValue> class in C# provides a thread-safe collection of key/value pairs that is optimized for high concurrency. It supports concurrent read and write operations without locking, making it ideal for scenarios where multiple threads need to interact with a shared dictionary. Understanding how to use ConcurrentDictionary<TKey, TValue> effectively can help you manage collections of data in multi-threaded applications.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top