Channels or mutexes: how do you decide?
Question 208MediumGo 1.22 to 1.25
The Go proverb says "Don't communicate by sharing memory; share memory by communicating", but the practical rule is:
- Channels are for passing ownership of data, coordinating goroutine lifecycles, distributing work, pipelines, signaling events and cancellation, and combining waits with
selectand timeouts. - Mutexes are for protecting shared state such as caches, counters, and maps, where the operation is "read or modify in place". They are simpler and usually faster, taking tens of nanoseconds uncontended, whereas a channel op needs a lock plus possible goroutine park and wake.
- Atomics are for single-word counters and flags.
// Overkill: a channel-guarded counter
type ChanCounter struct{ inc chan struct{}; get chan int }
// Idiomatic
type Counter struct{ n atomic.Int64 }
func (c *Counter) Inc() { c.n.Add(1) }
func (c *Counter) Load() int64 { return c.n.Load() }
What the interviewer wants is pragmatism, not dogma. Mention that the standard library itself (e.g. net/http, database/sql) mostly uses mutexes and atomics and saves channels for coordination. Channel-heavy designs have more ways to deadlock and leak.
More on Channels & select
- Q206Why is
chan struct{}preferred for signaling, and how doescloseact as a broadcast? - Q207What does this print? (len and cap of channels)
- Q209Explain the pipeline pattern and how to cancel it properly.
- Q210What happens if you send on a closed channel inside a
selectwith adefault? - Q211Does an unbuffered channel give a happens-before guarantee? What does the memory model say about channels?
- Q212Implement a generic "first response wins" (hedged request / race) function.