Use mutexes to protect shared data structures accessed by multiple goroutines. Use channels to communicate data or signal events between goroutines. Do not mix both patterns on the same resource.
Protecting a shared cache, counter, or map from concurrent reads and writes
RWMutex when reads dominate: many concurrent readers, rare writers
Guarding a struct's internal state in a concurrent-safe type
Short critical sections where channel overhead would be wasteful
Passing ownership of data between goroutines — only one goroutine holds the data at a time
Signaling events: task completion, shutdown, rate limiting tokens
Pipeline and fan-out patterns where data flows through stages
Worker pool coordination: jobs channel distributes work, results channel collects output