When would you use `ConcurrentDictionary` over `Dictionary`?

4 minintermediate.NETconcurrencycollectionsthread-safety

Quick Answer

Use `ConcurrentDictionary<TKey,TValue>` when multiple threads read and write the same dictionary concurrently — it provides thread-safe, fine-grained locking and atomic operations like `GetOrAdd`/`AddOrUpdate` without external locks. A plain `Dictionary<TKey,TValue>` is not thread-safe for concurrent writes and would require your own locking. For single-threaded or read-only-after-build scenarios, `Dictionary` is faster and lighter.

Detailed Answer

Use ConcurrentDictionary<TKey, TValue> when:

  1. Multiple threads access the collection simultaneously
  2. Thread-safety is required
  3. High-performance concurrent operations needed

Use regular Dictionary<TKey, TValue> when:

  1. Single-threaded scenarios
  2. External synchronization is handled
  3. Performance is critical (Dictionary is faster in single-threaded)
// Regular Dictionary - NOT thread-safe
var dict = new Dictionary();
// Multiple threads accessing this will cause issues!

// ConcurrentDictionary - Thread-safe
var concurrentDict = new ConcurrentDictionary();

// Thread-safe operations
Parallel.For(0, 1000, i =>
{
    concurrentDict.TryAdd(i, $"Value{i}");
});

// Atomic operations
concurrentDict.AddOrUpdate(
    key: 1,
    addValue: "New",
    updateValueFactory: (key, oldValue) => oldValue + "_Updated"
);

// GetOrAdd - atomic operation
var value = concurrentDict.GetOrAdd(5, key => $"Value{key}");

// TryRemove - thread-safe removal
concurrentDict.TryRemove(1, out string removed);

// Update with condition
concurrentDict.TryUpdate(
    key: 2,
    newValue: "NewValue",
    comparisonValue: "OldValue"
);

Key Differences:

ConcurrentDictionary Advantages:

  • Built-in thread-safety
  • Atomic operations (AddOrUpdate, GetOrAdd)
  • Lock-free reads in most cases
  • Fine-grained locking (only locks affected buckets)

ConcurrentDictionary Disadvantages:

  • Higher memory overhead
  • Slightly slower in single-threaded scenarios
  • More complex API

Common Scenarios:

// Caching in multi-threaded applications
public class CacheService
{
    private readonly ConcurrentDictionary _cache = new();

    public object GetOrAdd(string key, Func factory)
    {
        return _cache.GetOrAdd(key, factory);
    }
}

// Counting in parallel processing
var wordCounts = new ConcurrentDictionary();
Parallel.ForEach(documents, doc =>
{
    foreach (var word in doc.Split())
    {
        wordCounts.AddOrUpdate(word, 1, (key, count) => count + 1);
    }
});

// Session storage in web applications
private static ConcurrentDictionary _sessions = new();