liveBook · Manning

Persistent map

As explained in C# Concurrency: Asynchronous and multithreaded programming

ImmutableDictionary<TKey,TValue> is a .NET generic dictionary whose contents cannot be changed after creation and whose modification methods return modified copies.

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

How C# Concurrency: Asynchronous and multithreaded programming covers it

How deep it goes
Explains read and copy-on-write APIs, builders, variable synchronization, and compare-and-retry updates, while deferring implementation internals.
Examples
Demonstrates book inventory purchases, SetItem-based copies, and an ImmutableInterlocked.TryUpdate retry loop.
What it assumes
Assumes familiarity with Dictionary<TKey,TValue>, concurrent threads, and generic collection operations; teaches immutable update behavior explicitly.
What you can do after
Apply ImmutableDictionary APIs and diagnose unsafe shared-variable updates by switching compound modifications to ImmutableInterlocked.
How it connects
Connects immutable dictionaries with Dictionary<TKey,TValue>, ConcurrentDictionary<TKey,TValue>, ImmutableInterlocked, builders, and thread safety.

Also explained in

Compare how 2 books explain persistent map →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C# Concurrency: Asynchronous and multithreaded programming. The section links point to the parts of the book it draws on.