Skip to content

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 RwLock solely 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<...>>?

References

  • Rust Standard Library
  • Tokio Documentation
  • The Rust Performance Book