Locking gives one thread at a time access to a critical section that uses a shared resource. A mutex provides this mutual exclusion: each thread acquires the same mutex before accessing the resource and releases it afterward. Protecting every access this way prevents concurrent updates from corrupting shared data. Keep the critical section short so other threads spend less time waiting. section 8.1 section 8.1.1
A shared counter
Suppose 100 threads each increment one counter. Wrap the counter in a Rust std::sync::Mutex; each thread calls lock(), increments the value while holding the guard, then leaves the scope to unlock it. The increments are safe, but they cannot execute in parallel because every thread needs the same lock. section 8.1 section 5.1
use std::sync::Mutex;
use std::thread;
fn main() {
let counter = Mutex::new(0);
thread::scope(|s| {
for _ in 0..100 {
s.spawn(|| {
let mut counter = counter.lock().unwrap();
*counter += 1;
// Mutex is unlocked implicitly here.
});
}
});
}
The listing shows the complete scoped-thread counter example, including where the mutex is acquired and implicitly released. section 8.1
Exclusivity has a latency cost. A thread that cannot acquire a mutex waits; blocking it and waking it involves the operating system. Contention also forces otherwise concurrent work to run serially. Lock operations add overhead of their own, and contention can bring context switches, atomic-instruction costs, and memory-system effects. The resulting delays matter especially to tail latency. section 8.1.1 section 8.2.1
Measure contention, not just lock cost
In a shared-counter benchmark, a mutex lock-and-unlock takes about 7.4 ns with one thread, about 603 ns with 10, and about 5.28 µs with 100. Measure at realistic thread counts: an inexpensive uncontended operation can become expensive when many threads need the same critical section. section 8.2.1
Choose the synchronization strategy against the workload:
- Use a read–write lock when concurrent reads are useful; writes still require exclusive access and make readers wait. section 8.1.2
- Partition shared data to reduce the number of clients competing for each lock. Check for skew: hot partitions can make latency worse. section 5.1
- Consider wait-free synchronization for suitable low-latency structures, but account for the atomic operations and memory barriers needed to make concurrent access safe. section 8.6 section 8.7
Also watch for failure modes beyond raw overhead: priority inversion lets a lower-priority lock holder delay higher-priority work; convoying queues threads behind a lock; deadlock can stop progress altogether. Locking remains a straightforward way to make shared access safe, but its cost depends on contention and workload shape—not merely on how fast an uncontended lock call appears. section 8.2 section 8.2.1

















