Concurrency · 3 MIN READ
A mutex is for state; a channel is for handing over
Choose between a mutex and a channel for a given problem.
A channel is useful for transferring values and coordinating ownership. A mutex protects shared state that remains in place. Choose the mechanism that makes the invariant and its owner easiest to understand; neither mechanism is always faster.
Keep the mutex next to the data it protects and use the same locking rule for every read and write participating in the invariant. A small, clearly bounded critical section is easier to maintain than a lock whose protected state is scattered.
SEE THE IDEA
One lock protects one shared invariant
- 01
Enter
gomu.Lock()Wait for exclusive access
- 02
Update
gocounter++Read and modify the protected state
- 03
Leave
gomu.Unlock()Allow the next owner to enter
Every access participating in the shared invariant must follow the same locking rule.
In practice
Keep the critical section short and make lock ownership easy to see. A deferred unlock protects early returns, while an explicit unlock may be clearer around a small block. Use a channel when transferring ownership expresses the operation better than protecting shared mutable state.
Watch out
Protect reads as well as writes. Copying a used mutex or taking the same mutex twice can break the synchronization contract.
Work through the exercise
package main
import "sync"
// Counter should be safe for many goroutines at once.
type Counter struct {
mu sync.Mutex
count map[string]int
}
func NewCounter() *Counter {
return nil
}
func (c *Counter) Add(key string) {}
func (c *Counter) Get(key string) int { return 0 }
func main() {}Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.
Progress stays in this browser. No account needed.