ImmutableInterlocked synchronizes updates to variables that reference immutable collections. It avoids protecting the entire operation with a lock while preserving immutable snapshots for readers. section 13.3.2 section 13.3.3
Use the API with an all-or-nothing discipline:
- Use AddOrUpdate, GetOrAdd, TryAdd, TryRemove, and TryUpdate with ImmutableDictionary<TKey,TValue>. section 13.3.3
- Use Push and TryPop with ImmutableStack<T>, or Enqueue and TryDequeue with ImmutableQueue<T>. section 13.3.3
- Retry after TryUpdate returns false: reread the value, validate the new state, and attempt the replacement again. section 13.3.3
- Do not mix ImmutableInterlocked with ordinary immutable-collection methods or builders for concurrent modifications, and do not update multiple collections as one transaction. section 13.3.4 section 13.3.3
Selling the last available copy safely
A stock-management method reads the current quantity, rejects missing or empty inventory, and calls ImmutableInterlocked.TryUpdate to replace the referenced dictionary only if the observed quantity is still current. A failed update loops until another thread wins or inventory reaches zero. 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
}
}
Runnable C# listing showing the TryUpdate retry loop for thread-safe inventory management. section 13.3.3
