Concurrency Fundamentals Best Practices
Share memory by communicating - and when to use locks.
Search across all documentation pages
Share memory by communicating - and when to use locks.
These rules keep goroutines bounded, channels deadlock-free, and shared state race-free from dev laptops through production services.
go, channels, or sync types.go test -race ./... and golangci-lint on touched packages before merge.go needs a path to completion: WaitGroup, close(ch), or context cancel.main return while workers still run. Exit kills the process without waiting for background goroutines.runtime.NumGoroutine() in soak tests. Monotonic growth signals leaks on blocked channel receives.range or v, ok := <-ch.sync.Once or a dedicated coordinator goroutine.select + default spin loops. Busy polls burn CPU; block or use tickers/context deadlines.context.WithTimeout over time.After in server loops. Reuse timers in hot paths to reduce allocations.defer mu.Unlock() immediately after Lock. Survives panics and early returns in handlers.go test -race on CI for packages using concurrency. Treat new race reports as release blockers.-count=50 -race. Interleavings surface races that pass once.-race binaries to latency-sensitive prod. Use staging soak with race builds instead.context.Context through concurrent call trees. Cancellation stops downstream goroutines when clients disconnect.Channels when transferring ownership or staging pipeline work.
Mutexes when a small shared struct sees frequent updates.
Start at 0 or worker count.
Increase only when profiles show blocking on full buffers.
Tie background work to r.Context().
Return when the client disconnects and workers observe ctx.Done().
No - benchmark Mutex+map first.
Use sync.Map for proven read-mostly caches only.
net/http already does per request.
Extra goroutines need justification and cancellation.
When one writer owns mutation or a mutex serializes access.
Communicate first; share when simpler and measured.
Check close ownership, WaitGroup pairing, lock scope, and whether -race tests exist.
No - only unsynchronized memory access.
Add timeouts and structured shutdown tests separately.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026