ImmutableDictionary<TKey,TValue> is an immutable counterpart to Dictionary<TKey,TValue>. Reads use ContainsKey, the indexer, and TryGetValue; operations such as Add, Remove, and SetItem return a modified dictionary while leaving the original unchanged. section 13.3.4
Immutability makes each dictionary instance safe to read concurrently, but the variable referencing the current instance can still be replaced concurrently. Protect that replacement with ImmutableInterlocked rather than assuming the collection alone makes a compound operation safe. section 13.3.2 section 13.3.3
Use the surrounding APIs according to the update pattern:
- Batch changes with AddRange, RemoveRange, or UpdateItems so one operation creates one modified dictionary. section 13.3.4
- Use ToBuilder for several different or difficult-to-group changes, then call ToImmutable; the builder is neither immutable nor thread-safe. section 13.3.4
- Use ImmutableInterlocked.AddOrUpdate, GetOrAdd, TryAdd, TryRemove, or TryUpdate when threads may replace the referenced dictionary. section 13.3.3
Concurrent inventory decrement
A safe purchase operation reads the current quantity, rejects missing or empty stock, and repeatedly calls ImmutableInterlocked.TryUpdate. If another thread changed the quantity, it rereads and retries; otherwise it replaces the dictionary with a copy containing one fewer item. section 13.3.3
Listing 13.18 Thread-safe stock management with ImmutableInterlocked
private ImmutalbeDictionery<string,int> _bookIdToQuantity = new();
public bool TryToBuyBook(string bookId)
{
while(true)
{
if(!_bookIdToQuantity.TryGetValue(bookId,
out var copiesInStock)) #1
Return false;
if(copiesInStock == 0)
return false;
if(ImmutableInterlocked.TryUpdate( #2
ref _bookIdToQuantity, bookId, #2
copiesInStock-1, copiesInStock)) #2
return true; #3
} #4
}
}
Inspect the complete retry-based inventory implementation using ImmutableInterlocked.TryUpdate. section 13.3.3

