0
0
CsharpHow-ToBeginner · 4 min read

How to Use ConcurrentDictionary in C# for Thread-Safe Collections

Use ConcurrentDictionary<TKey, TValue> in C# to store key-value pairs safely across multiple threads without locks. It provides thread-safe methods like TryAdd, TryGetValue, and AddOrUpdate to manage data concurrently.
📐

Syntax

The ConcurrentDictionary<TKey, TValue> class is a thread-safe collection for storing key-value pairs. It supports concurrent read and write operations without needing explicit locks.

  • TKey: Type of the keys.
  • TValue: Type of the values.
  • Common methods include TryAdd(key, value), TryGetValue(key, out value), AddOrUpdate(key, addValueFactory, updateValueFactory), and TryRemove(key, out value).
csharp
ConcurrentDictionary<TKey, TValue> dictionary = new ConcurrentDictionary<TKey, TValue>();
💻

Example

This example shows how to create a ConcurrentDictionary, add items safely, update values, and retrieve them in a thread-safe way.

csharp
using System;
using System.Collections.Concurrent;

class Program
{
    static void Main()
    {
        var dictionary = new ConcurrentDictionary<string, int>();

        // Add a key-value pair
        bool added = dictionary.TryAdd("apple", 3);
        Console.WriteLine($"Added apple: {added}");

        // Try to add the same key again
        added = dictionary.TryAdd("apple", 5);
        Console.WriteLine($"Added apple again: {added}");

        // Update or add a key
        int newValue = dictionary.AddOrUpdate("apple", 1, (key, oldValue) => oldValue + 2);
        Console.WriteLine($"Updated apple value: {newValue}");

        // Retrieve a value
        if (dictionary.TryGetValue("apple", out int value))
        {
            Console.WriteLine($"Value for apple: {value}");
        }

        // Remove a key
        bool removed = dictionary.TryRemove("apple", out int removedValue);
        Console.WriteLine($"Removed apple: {removed}, value was: {removedValue}");
    }
}
Output
Added apple: True Added apple again: False Updated apple value: 5 Value for apple: 5 Removed apple: True, value was: 5
⚠️

Common Pitfalls

Common mistakes when using ConcurrentDictionary include:

  • Assuming TryAdd always adds the item; it returns false if the key exists.
  • Using indexer [key] to add or update without thread-safe guarantees for complex updates.
  • Not using AddOrUpdate or GetOrAdd for atomic operations, which can cause race conditions.

Always use the provided thread-safe methods to avoid data corruption.

csharp
var dict = new ConcurrentDictionary<string, int>();

// Wrong: This is not atomic and can cause race conditions
if (!dict.ContainsKey("key"))
{
    dict["key"] = 1; // Another thread might add "key" here
}

// Right: Use GetOrAdd for atomic add or get
int value = dict.GetOrAdd("key", 1);
📊

Quick Reference

MethodDescription
TryAdd(key, value)Adds a key-value pair if the key does not exist.
TryGetValue(key, out value)Gets the value for a key if it exists.
AddOrUpdate(key, addValueFactory, updateValueFactory)Adds or updates a key with a value atomically.
GetOrAdd(key, valueFactory)Gets the value if key exists or adds it atomically.
TryRemove(key, out value)Removes a key and returns its value if it exists.

Key Takeaways

Use ConcurrentDictionary for thread-safe key-value storage without manual locks.
Prefer AddOrUpdate and GetOrAdd methods for atomic operations to avoid race conditions.
TryAdd returns false if the key already exists; it does not overwrite.
Avoid checking ContainsKey before adding; use thread-safe methods instead.
ConcurrentDictionary is ideal for multi-threaded environments where data safety is critical.