A critical region is the code that must run without another goroutine accessing the same shared state concurrently. In Go, a mutex marks its boundary: lock before the access, then unlock afterward. For two goroutines incrementing one variable, `i++` belongs inside that boundary. Otherwise, both can read the old value and write back the same incremented value. section 8.4.1
Protect the increment
Each goroutine must use the same `sync.Mutex` around `i++`: `mutex.Lock(); i++; mutex.Unlock()`. This protects the read, increment, and write together. For a supported numeric type, `atomic.AddInt64(&i, 1)` is another option. A channel can instead send increment requests to a single goroutine that owns the variable. Choose based on what must be shared: atomic operations cover specific numeric types, while a mutex can protect a larger operation or data structure. section 8.4.1
A common mistake is to lock only long enough to assign a map or slice to a local variable, then use that variable after unlocking. The assignment does not copy the underlying data. If another goroutine mutates it, the supposedly separate work still accesses shared state; a concurrent balance update and average calculation produces a data race detectable with `-race`. section 9.10
func (c *Cache) AverageBalance() float64 {
c.mu.RLock()
balances := c.balances #1
c.mu.RUnlock()
sum := 0.
for _, balance := range balances { #2
sum += balance
}
return sum / float64(len(balances))
}
The read lock ends before iteration. `balances := c.balances` retains access to the shared map rather than making an independent copy. section 9.10
Set the boundary around the data actually being accessed:
- For inexpensive iteration, hold `RLock` through the entire average calculation, then release it. This protects every read of the map. section 9.10
- To calculate outside the lock, allocate a new map and copy its entries while holding `RLock`; only then unlock and iterate over the copy. section 9.10
The lock itself must also be shared. If a struct contains a `sync.Mutex`, a value-receiver method copies that mutex on each call. The method may visibly call `Lock` and `Unlock`, yet concurrent calls no longer enter one shared critical region. A pointer receiver avoids copying the struct; alternatively, store a pointer to the mutex and initialize it before use. `go vet` can flag a method that passes a lock by value. section 9.14








