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:
- Multiple threads access the collection simultaneously
- Thread-safety is required
- High-performance concurrent operations needed
Use regular Dictionary<TKey, TValue> when:
- Single-threaded scenarios
- External synchronization is handled
- 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();