Shared State in Go Concurrency

Protect shared data with sync.Mutex and sync.RWMutex, run setup once with sync.Once, choose between channels and mutexes, and find races with -race.

As soon as two goroutines touch the same variable, you need a rule for who can change it and when. This section covers the locking tools in the sync package, how to choose between them and channels, and how to find the bugs that appear when the rule is broken.

sync.Mutex and sync.RWMutex

A mutex lets one goroutine at a time into a critical section. Put it inside the struct it protects and pair every Lock with defer Unlock.

c.mu.Lock()
defer c.mu.Unlock()
c.n++

sync.Once

Run setup exactly once across goroutines, with sync.OnceValue and sync.OnceValues returning the result directly.

Channel vs Mutex

Mutexes for state that stays in place, channels for values and signals that move between goroutines.

The Race Detector

go test -race ./... reports WARNING: DATA RACE with both conflicting lines.

Concurrent map writes are fatal:

fatal error: concurrent map writes cannot be recovered. Protect shared maps with a mutex.

Summary

Give every shared value one clear protection, keep critical sections short, and run the race detector on your tests.

sync.Mutex and sync.RWMutex in Go

Protect shared data from concurrent goroutines with sync.Mutex, allow many readers with sync.RWMutex, and avoid the usual locking mistakes.

sync.Once in Go

Run setup code exactly once even when many goroutines ask for it, with sync.Once, sync.OnceValue and sync.OnceValues.

Channel vs Mutex in Go - Which One to Use

A practical guide to choosing between channels and sync.Mutex in Go, with the same problem solved both ways and clear rules for each.

The Go Race Detector

Find data races in Go programs with the -race flag, read the WARNING DATA RACE report, and understand what the race detector can and cannot catch.