Mutex, RWMutex, WaitGroup, Once, atomic, errgroup.
Mutex and WaitGroup
Channels are the high-level concurrency tool. But sometimes you really do want
plain "share state, lock it" — and that's what the sync package is for.
sync.Mutex
A mutex protects shared state. Lock before reading or writing, unlock when done
(usually via defer).
Without the mutex, multiple goroutines would race on c.n++ and you'd get a
nondeterministic answer (and go test -race would scream). With it, the
increments are serialized.
sync.RWMutex
If your shared state is read often and written rarely, an RWMutex lets many
readers in at once but only one writer:
mu.RLock(); v := cache[key]; mu.RUnlock()
mu.Lock(); cache[key] = newVal; mu.Unlock()
Use a plain Mutex first; only switch to RWMutex if profiling shows lock
contention. The bookkeeping in RWMutex makes it slower for low-contention cases.
sync.WaitGroup
We saw WaitGroup already — three methods:
wg.Add(n)— "expectnmore goroutines to finish".wg.Done()— "I'm done". Subtract one.wg.Wait()— block until the counter is zero.
The pattern is always the same:
var wg sync.WaitGroup
for _, x := range work {
wg.Add(1)
go func(x Work) {
defer wg.Done()
process(x)
}(x)
}
wg.Wait()
Add happens before the go, so the counter is high before any goroutine
has a chance to start. Done always runs (because of defer), even if process
panics.
Channels or mutex — when do I use which?
Use a channel to pass ownership of data between goroutines, or to signal events. Use a mutex to protect shared state read and written by several goroutines.
| Need | Reach for |
| ------------------------------------------ | -------------- |
| Pipeline of data through stages | channel |
| Many goroutines incrementing a counter | mutex (or atomic) |
| Signaling "this is done" | chan struct{} or WaitGroup |
| Caching with multiple readers | RWMutex |
| Bounding concurrency (semaphore) | buffered channel |
The classic mistake is reaching for a mutex when a channel would be simpler, or vice versa. Whichever you choose, stick with one model per piece of state.