A mutex provides mutual exclusion: at most one goroutine can enter a protected critical section at a time. Use a lock when goroutines must access shared mutable state, not simply because a program uses concurrency. Channels are often a better fit when goroutines need to pass results or coordinate work. section 8.4.1 section 8.3
Protect the entire update
Two goroutines executing `i++` without synchronization can both read 0 and both write back 1. Put the read, increment, and write inside the same lock in each goroutine: var mu sync.Mutex var i int go func() { mu.Lock() i++ mu.Unlock() }() // The other goroutine uses the same mu around its i++. After both updates finish, `i` is 2. For supported numeric types, an atomic operation is another option. Alternatively, send increment requests through a channel and let one goroutine own `i`. section 8.4.1
A lock prevents conflicting access only where the lock is held. In a cache backed by a map, locking while assigning `balances := c.balances` does not make later iteration safe: the new variable still refers to the same map data. A slice assignment similarly retains a shared backing array. section 9.10
Keep protection aligned with the data actually accessed:
- For a short calculation over a shared map, hold `RLock` through the entire iteration and calculation, then call `RUnlock`. Writers use `Lock` and `Unlock`. section 9.10
- If the work outside the lock is substantial, copy every map entry while holding `RLock`, unlock, and calculate from the independent copy. Copying only the map variable is insufficient. section 9.10
func (c *Cache) AverageBalance() float64 {
c.mu.RLock()
m := make(map[string]float64, len(c.balances)) #1
for k, v := range c.balances {
m[k] = v
}
c.mu.RUnlock()
sum := 0.
for _, balance := range m {
sum += balance
}
return sum / float64(len(m))
}
The map-copy implementation shows which work must stay inside the read lock before calculation can safely continue after unlocking. section 9.10
Do not copy the lock
A struct can contain both `mu sync.Mutex` and a shared map. If its `Increment` method has a value receiver, each call copies the struct—and the mutex—while the map data remains shared. The goroutines then lock different mutexes, so `-race` can report a data race. Use a pointer receiver, `func (c *Counter) Increment(name string)`, to keep one shared lock. A pointer-valued mutex field is another option, but it must be initialized before `Lock` is called. `go vet` can flag a method that passes a lock by value. section 9.14
Locking can remove a data race without making every result deterministic. If two properly locked goroutines assign different values to the same variable, the final value can still depend on which assignment runs last. section 8.4.1 section 8.7

















