Go Concurrency Distilled
10 hours ago
- Goroutines are lightweight concurrent functions started with the 'go' keyword; the Go runtime schedules them across OS threads, and main is also a goroutine.
- Use sync.WaitGroup to wait for goroutines to finish; Add increments the counter, Done decrements it, and Wait blocks until the counter reaches zero.
- Channels allow goroutines to pass values; they can be buffered or unbuffered, directional (send/receive), and closing a channel signals completion or stops further sends.
- The select statement manages multiple channel operations: it blocks until a case is ready, randomly picks among ready cases, and supports a default case for non-blocking behavior.
- Pipelines connect goroutines via channels; a done channel or context can signal cancellation, and error handling in pipelines can use timeouts or dedicated result channels.
- The context package is used for cancellation and timeouts; child contexts can shorten parent deadlines but not extend them, and context.WithValue passes call-scoped values.
- Data races occur when multiple goroutines access shared data with at least one modification; the race detector (go run -race) helps find them, and prevention includes channels or mutexes.
- Race conditions are logical bugs from unpredictable interleaving of concurrent operations; they aren't caught by the race detector and may require atomic compare-and-swap or mutexes.
- Mutexes (sync.Mutex, sync.RWMutex) protect shared data; RWMutex allows multiple readers or one writer, and channels can also be used for mutual exclusion.
- Semaphores limit concurrent access to resources (using buffered channels or sync.Semaphore); a rendezvous coordinates two goroutines, while a barrier allows N goroutines to meet.
- sync.Cond provides condition-variable signaling with Wait/Signal/Broadcast; for more flexible notifications, channels can implement publish/subscribe patterns.
- sync.Once ensures a function runs exactly once for one-time initialization; convenience functions like OnceFunc are also provided.
- sync.Pool is an object pool for reusing temporary objects; it has no size limit, and items may be discarded, so it's only for short-lived objects without strong lifetime guarantees.
- Atomic operations in sync/atomic (e.g., atomic.Int32) provide thread-safe single operations like Load, Store, Add, and CompareAndSwap; compositions of atomics are not atomic and may need mutexes.
- Testing concurrent code can be done via synchronization handles (e.g., channels) or the synctest package, which simulates time and lets you wait for goroutine states.
- Scheduling: the Go runtime runs many goroutines on fewer OS threads; GOMAXPROCS controls thread count, and the scheduler preempts long-running goroutines to ensure fairness.
- Diagnostics include runtime/metrics for runtime statistics, pprof for CPU/heap profiling, and runtime/trace for tracing concurrency events; automatic tracing can be set up with a sliding window.
- Final recommendations: use goroutines for tasks, channels/select for communication, wait groups for sync, mutexes for shared-state protection, condition variables for signaling, and Once for one-time init.