Why this matters

The bug. _hits = _hits + 1 looks atomic but compiles to *load → add → store*. Two threads can interleave: both load 5, both store 6, one increment is lost. Under heavy concurrency you can lose a meaningful fraction of writes — this is exactly how analytics counters silently undercount.

The fix. Interlocked.Increment(ref _hits) is a single atomic CPU instruction (LOCK XADD on x86). For richer state, wrap a lock (this) { ... } around the read-modify-write block.

Subtlety. Even in single-process code, the JIT and CPU can reorder reads/writes — the volatile keyword prevents tearing on int/bool reads but does *not* make ++ atomic. Always reach for Interlocked or a lock.

Review heuristic

Every check-then-act over shared state is a race waiting for production load. Look for read-then-write pairs that aren't inside a transaction, a lock, or an atomic CAS. If you can articulate the bug as "if two requests arrive at the same time, both will...", it's a race.

External reference: CWE-362: Concurrent Execution using Shared Resource.