A critical region, also called a critical section, is the part of concurrent code that uses shared state and must run without interference from another execution. If goroutines overlap there, a read-modify-write operation such as changing a shared bank balance can produce unpredictable results—a race condition. section 3.3.1 section 3.5
A shared balance can drift
Two goroutines repeatedly add and subtract $10 from the same balance. Although the intended final balance is $100, separate runs can finish with radically different values because each update consists of multiple instructions that may interleave. The Stingy and Spendy listing and race-condition figure make the conflicting execution order visible. section 3.3.1
Protect a critical region with a mutex: lock immediately before accessing the shared resource, execute the protected work, then unlock. Only one goroutine can acquire the lock; contenders wait until it is released. In Go, `sync.Mutex` supplies `Lock()` and `Unlock()` for these boundaries. section 4.1.1 section 4.1
Listing 4.1 Stingy’s and Spendy’s functions using a mutex
package main import ( "fmt" "sync" "time" ) func stingy(money *int, mutex *sync.Mutex) { #1 for i := 0; i < 1000000; i++ { mutex.Lock() #2 *money += 10 mutex.Unlock() #3 } fmt.Println("Stingy Done") } func spendy(money *int, mutex *sync.Mutex) { #1 for i := 0; i < 1000000; i++ { mutex.Lock() #2 *money -= 10 mutex.Unlock() #3 } fmt.Println("Spendy Done") } #1 Accepts a pointer to the shared mutex struct #2 Locks the mutex before entering the critical section #3 Unlocks after exiting the critical section
The paired functions show the smallest useful boundary: lock around each shared balance update, then unlock before continuing. section 4.1.1
Choose boundaries around the resource rather than around an entire goroutine:
- Keep unrelated, lengthy work outside the lock. Locking a whole document download serializes work that could safely run concurrently. section 4.1.2
- Protect both writes and reads of the shared value when they can conflict with concurrent updates. section 4.1.1
- Do not use scheduler yielding as synchronization. `runtime.Gosched()` can alter timing but cannot guarantee that another execution will not interfere. section 3.3.2
- Consider readers–writer mutexes for read-heavy shared resources: concurrent readers may proceed, while a writer receives exclusive access. section 4.4
A critical region should be as small as correctness permits, but not smaller than the operations that must remain coordinated. Excessive locking and unlocking has a cost; holding a lock too long turns concurrent execution into sequential execution. When shared-memory protection makes data flow hard to follow, message passing can offer a simpler alternative. section 4.1.2 section 8.2.1








