When would you use sync.RWMutex over sync.Mutex, and when can RWMutex actually be slower?
Question 221MediumGo 1.22 to 1.25
RWMutex allows many concurrent readers or one writer. It pays off only when reads heavily outnumber writes and the critical section is long enough that letting readers run in parallel matters.
It can be slower than a plain Mutex because:
RLock/RUnlockatomically update a shared reader counter. On many cores that cache line bounces between CPUs, so tiny critical sections scale worse than with aMutex.- It has more bookkeeping (reader count, writer wait, two semaphores).
- Writers get priority: once a writer calls
Lock, newRLockcalls block until the writer has finished. With frequent writes you get little read parallelism.
type Config struct {
mu sync.RWMutex
vals map[string]string
}
func (c *Config) Get(k string) (string, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
v, ok := c.vals[k]
return v, ok
}
func (c *Config) Set(k, v string) {
c.mu.Lock()
defer c.mu.Unlock()
c.vals[k] = v
}
What the interviewer is looking for: you benchmark before choosing (go test -bench -cpu=1,4,16). For read-mostly data that is replaced as a whole, atomic.Pointer[T] with copy-on-write usually beats both locks.
More on Concurrency Patterns & sync
- Q222Explain sync.Mutex "normal mode" vs "starvation mode".
- Q223Is sync.Mutex reentrant? What happens with a recursive RLock on an RWMutex?
- Q224What does this print, and why? (copying a lock)
- Q225What is the classic sync.WaitGroup bug, and what does wg.Go (Go 1.25) change?
- Q226Explain sync.Once semantics, including panics, and the OnceFunc / OnceValue / OnceValues helpers.
- Q227When would you use sync.Cond, and why must Wait always be called in a loop?