C# race conditions explained: lost updates, check-then-act, Interlocked and concurrent collections

C# Async & Multithreading November 13, 2026 csharp

Common forms: lost update (++), check-then-act, and a Dictionary modified from multiple threads (which can get corrupted internally).

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// ======================================================================
// Q: What is a race condition? Show an example.
//
// Common forms: lost update (++), check-then-act, and a Dictionary modified from
// multiple threads (which can get corrupted internally). Fixes: immutable data,
// Interlocked, lock, concurrent collections, or no shared state at all (scoped
// services). In the database the same problem is solved with an atomic UPDATE or
// a concurrency token (Q60-Q61).
// ======================================================================

public class Stats                        // singleton: shared by all requests
{
    private int _orders;
    public void OnOrder() => _orders++;   // ✗ read-modify-write: with 2 threads one increment is lost
}                                         // ✓ Interlocked.Increment(ref _orders);

// ✗ check-then-act: two threads both see "not there" and both load it
if (!_cache.ContainsKey(sku)) _cache[sku] = await LoadAsync(sku);