While mutex locks prevent race conditions, they are slow. Locking a mutex requires a system call to the operating system kernel. If the lock is held, the OS puts the thread to sleep, which triggers a context switch (saving thread registers and reloading another thread). This takes hundreds of nanoseconds.
In latency critical systems, we avoid locks entirely and use hardware supported lock free atomic operations.
The Cost of Mutex Context Switches
When a thread is put to sleep by the OS scheduler, the CPU cache lines associated with that thread are cleared to make room for the incoming thread. When your thread wakes up again, it experiences cache misses, degrading overall system execution speed.
Atomics: Hardware Level Thread Safety
Modern CPU architectures (like x86_64 and ARM) support atomic operations directly at the silicon level using hardware instructions (such as Compare And Swap, or CAS). C++ exposes this hardware capability using std::atomic (from the <atomic> header).
An atomic variable performs read, modify, and write operations as a single, uninterrupted hardware instruction. No mutex locks, no context switches, and no thread sleep calls are involved:
Memory Barriers and Cache Sync
Atomics do more than prevent race conditions; they enforce compiler and hardware memory barriers. When you write to an atomic variable, C++ ensures that the CPU caches across all cores synchronize their data blocks, preventing other threads from reading outdated values.
* The Sugar Jar Metaphor: Storing variables with mutexes is like a coffee shop with a locked front door. Only one customer is let in at a time to add sugar to their cup. The shop is safe, but slow. Using atomics is like putting a single self serve sugar jar on a table. Customers line up and tap it directly. The shop keeps moving at full speed, and no lock cycles are created.