lock (obj) = Monitor.Enter / Exit inside try/finally.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // ====================================================================== // Q: How do lock and deadlocks work? Why can't you await inside a lock? // // - lock (obj) = Monitor.Enter / Exit inside try/finally. In .NET 9+ you write // private readonly Lock _lock = new();, which is faster. // - await inside a lock: compile error. The lock belongs to a thread, and the // continuation may run on a different thread. For an async critical section: // SemaphoreSlim(1, 1) (Q89). // - Don't lock on this, typeof(X) or a string: other code may lock on the same // object. Always lock on a private readonly field. // - Deadlock: thread A holds L1 and waits for L2, while B holds L2 and waits for // L1. Prevention: a consistent lock order, small critical sections, no // external calls (HTTP, database, callbacks) inside a lock, timeouts // (Monitor.TryEnter, WaitAsync(timeout)). // - Many readers, rare changes: instead of a lock, an immutable snapshot that is // replaced as a whole, as shown in the code. // ====================================================================== // Immutable FrozenDictionary snapshot instead of a lock private volatile FrozenDictionary<string, decimal> _prices = FrozenDictionary<string, decimal>.Empty; // Reload: build a new dictionary and swap the reference (atomic). Readers never lock. public void Reload(IEnumerable<PriceRow> rows) => _prices = rows.ToFrozenDictionary(r => r.Sku, r => r.Amount); public decimal? Get(string sku) => _prices.TryGetValue(sku, out var p) ? p : null; |