Working with collections in multi-threaded applications can be tricky. Most developers at first assume that Dictionaries are thread-safe.
If multiple threads read and write to a Dictionary at the same time, you risk corrupted state, and runtime exceptions.
Fortunately, .NET provides safer alternatives: ConcurrentDictionary and ImmutableDictionary.
Dictionary
: Fast, but Unsafe
A Dictionary
is optimized for single-threaded performance.
If multiple threads perform concurrent writes, it can throw exceptions such as:
var dict = new Dictionary<int, string>();
Parallel.For(0, 1000, i =>
{
dict[i] = $"Value {i}";
});
This may result in:
ConcurrentDictionary
: Thread-Safe, Mutable
The ConcurrentDictionary is designed for multi-threaded access.
var dict = new ConcurrentDictionary<int, string>();
Parallel.For(0, 1000, i =>
{
dict[i] = $"Value {i}"; // Safe and efficient
});
Key points
Uses fine-grained locking.
Thread-safe for both reads and writes.
Still mutable: once a value is set, any thread can overwrite it.
How is it immutable if it is thread-safe?
It manages concurrent access to its internal data structure through intelligent locking and lock-free techniques, allowing multiple threads to safely add, remove, or update key-value pairs.
Instead of locking the entire dictionary for every write operation, ConcurrentDictionary divides its internal data structure into segments or partitions. Each segment has its own lock.
When a thread needs to modify an item, it only acquires the lock for the specific segment containing that item.
This allows other threads to concurrently modify items in different segments, improving concurrency.
ImmutableDictionary
: Thread-Safe, Immutable
The ImmutableDictionary ensures safety through immutability.
using System.Collections.Immutable;
var dict = ImmutableDictionary<int, string>.Empty;
Parallel.For(0, 1000, i =>
{
dict = dict.SetItem(i, $"Value {i}"); // Each call creates a new dictionary
});
Key points
Immutable by design: once you have a reference, it never changes.
Always thread-safe, no locks needed.
Writes are slower (creates a new copy), but reads are extremely fast and safe.
![method]()
This is a benchmark result displaying the difference between using Dictionary, ConcurrentDictionary, and ImmutableDictionary in Multi-Thread apps.