Why Are Spinlocks Preferred for Short Critical Sections?

Spinlocks are preferred over sleeping locks for very short critical sections on multiprocessor systems because busy-waiting avoids the high overhead of context switching. When a thread cannot acquire a sleeping lock, the operating system suspends it and schedules another thread, which costs thousands of CPU cycles. If the lock is held for less time than it takes to perform two context switches, spinning in a tight loop is significantly faster.

The Cost of Context Switching

A sleeping lock, such as a mutex or semaphore, puts a waiting thread into a blocked state when the lock is already held. This process requires the operating system kernel to save the current thread's registers and memory mappings, update scheduling queues, and load a different thread onto the CPU. Once the lock holder finishes its work and releases the lock, the kernel must wake the waiting thread and schedule it again.

Let \(T_{ctx}\) represent the time required for a single context switch, and let \(T_{cs}\) represent the duration of the critical section. A sleeping lock incurs at least two context switches for a contended acquisition, giving a total overhead of \(2 \cdot T_{ctx}\). If \(T_{cs} < 2 \cdot T_{ctx}\), the thread would spend more time being put to sleep and woken up than it would simply waiting for the lock to become free.

Why Multiprocessors Make Spinning Viable

Spinlocks rely on busy-waiting, where a waiting thread repeatedly checks a shared memory location in a loop until the lock becomes available. On a uniprocessor system, spinning is counterproductive because the thread holding the lock cannot run while the waiting thread occupies the only CPU.

On a multiprocessor system, however, multiple threads execute simultaneously on different cores. While one core spins waiting for the lock, the core holding the lock continues executing its critical section without interruption. As soon as the lock holder releases the lock, the spinning core can acquire it almost immediately, often within a few dozen CPU cycles.

Hardware and Cache Considerations

Modern spinlock implementations minimize memory bus contention by using atomic instructions such as Compare-and-Swap (CAS) or Test-and-Set. To reduce unnecessary cache-coherence traffic, many spinlocks use a Test-and-Test-and-Set (TTAS) pattern, where the waiting core reads the lock's cached value locally until it observes the lock is free, and only then attempts an atomic write. For systems with many cores, queue-based spinlocks like MCS locks ensure each waiting core spins on its own local cache line, preventing cache-line bouncing.

When Sleeping Locks Are Still Better

Spinlocks are only optimal when the critical section is brief and non-blocking. If a thread holds a spinlock for a long duration, other cores waste substantial CPU cycles and power while spinning. Furthermore, a thread holding a spinlock should never sleep or be preempted, as doing so forces all other waiting cores to spin for the entire duration of the preemption. For longer operations or I/O-bound tasks, sleeping locks remain the better choice because they free the CPU to perform useful work.