C# thread safety: lock, SemaphoreSlim, Interlocked and concurrent collections

C# Language Interview Q&A November 13, 2026 csharp

Immutable data and no shared state: the best option.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ======================================================================
// Q: How do you make code thread-safe?
//
// - Immutable data and no shared state: the best option.
// - lock: for short critical sections. It does not allow await inside it (.NET
//   9+ has the System.Threading.Lock type).
// - SemaphoreSlim: for async critical sections or limiting concurrent
//   operations.
// - Interlocked: for atomic counters. ConcurrentDictionary, Channel<T> for
//   producer/consumer.
// - Watch out: a singleton service is shared across all requests, so it must be
//   thread-safe.
// ======================================================================

private readonly SemaphoreSlim _gate = new(1, 1);

public async Task RefreshTokenAsync(CancellationToken ct)
{
    await _gate.WaitAsync(ct);      // only one refresh at a time
    try { _token = await FetchTokenAsync(ct); }
    finally { _gate.Release(); }
}