Theme
Q046. When should you use a Mutex versus an RwLock?
Short Answer
Use a Mutex by default for shared mutable state: it has one access mode and usually lower bookkeeping overhead. Consider an RwLock when reads greatly outnumber writes, read critical sections are long enough to benefit from overlap, and the workload has been measured. An RwLock is not automatically faster for read-heavy code; writers, fairness policy, cache contention, and short critical sections can erase the advantage.
Deep Dive
A mutex admits one guard, whether the operation reads or writes. This makes invariants and latency easier to reason about. An RwLock admits multiple readers or one writer, which can improve throughput when concurrent readers perform meaningful work under the lock.
Costs of an RwLock include more complex state tracking and writer-reader coordination. A waiting writer may block new readers under a write-preferring policy. Other policies can starve writers. The standard library's exact priority policy is platform-dependent, while Tokio documents a fair, write-preferring queue.
Keep critical sections short and avoid I/O while holding either lock. If different fields are independent, redesigning ownership or partitioning state may matter more than switching lock types.
Internal Model
Both primitives use atomic state and may park a thread when acquisition cannot proceed. An RwLock additionally tracks reader counts and writer state. Unlocking can wake waiting threads, causing scheduler and cache-coherence work.
Contention, not merely lock acquisition, is usually the dominant concern.
Example
rust
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
fn main() {
let prices = Arc::new(RwLock::new(HashMap::from([("BTC", 60_000_u64)])));
let btc = {
let guard = prices.read().unwrap();
guard.get("BTC").copied()
};
prices.write().unwrap().insert("ETH", 3_000);
assert_eq!(btc, Some(60_000));
}Common Mistakes
- Choosing
RwLocksolely because reads are more frequent than writes. - Holding a read guard while attempting to acquire a write guard.
- Performing slow network or disk operations inside a critical section.
Follow-ups
- How can writer starvation occur?
- What does lock poisoning mean in
std::sync? - When is sharding better than one
RwLock<HashMap<...>>?
Related
- Q042. When should you use
Arc,Mutex,RwLock, atomics, or message passing? - Q047. How do
std::synclocks differ fromtokio::synclocks? - Q052. When should you use
DashMapversusRwLock<HashMap>?
References
- Rust Standard Library
- Tokio Documentation
- The Rust Performance Book