Go, End to End
Concurrency Patterns
Goroutines and channels are primitives. This chapter is the handful of patterns built from those primitives that show up constantly in real Go code: running a fixed pool of workers, combining several producers into one stream, and cleanly cancelling work that's no longer needed.
Worker pools
A worker pool runs a fixed, bounded number of goroutines pulling jobs off a shared channel, instead of spawning one goroutine per job (which, under enough load, is exactly the unbounded-goroutine risk flagged in the previous chapter).
Notice the channel directions in the signature: jobs <-chan int means worker can only receive from jobs, results chan<- int means it can only send to results. The compiler enforces this, a worker literally cannot accidentally send on jobs or receive from results, it's a real, checked contract, not just a naming convention.
Fan-in / fan-out
Fan-out is starting multiple goroutines to process work in parallel from a shared source. Fan-in is merging multiple result channels back into a single channel. The worker pool above is fan-out; here's the fan-in half, standalone, combining two independent producers into one stream.
context.Context for cancellation
`context.Context` is the idiomatic way to propagate "stop now" (or "stop after this deadline") through a call chain, especially one that spans goroutines. A function that does long-running or blocking work should accept a `Context` as its first parameter and check it periodically, so a caller further up the chain can cancel everything below it with one call.
`context.WithTimeout` (and its sibling `context.WithCancel`) returns both a context and a cancel function. Calling `cancel()`, or letting the timeout elapse, closes the context's internal Done channel, which every goroutine watching that context sees immediately via `<-ctx.Done()`. This is how you cancel an entire tree of goroutines with a single function call, rather than manually threading a stop signal through every layer yourself.
sync.WaitGroup
A `WaitGroup` is a counter: `Add(n)` increments it by `n`, `Done()` decrements it by one (almost always called via `defer` right at the top of the goroutine), and `Wait()` blocks until it hits zero. It's the standard way to wait for a known, fixed number of goroutines to finish, seen already in both examples above.
var wg sync.WaitGroup
wg.Add(1) // one goroutine to wait for
go func() {
defer wg.Done() // always defer this, right after starting the goroutine's real work
// ... do work ...
}()
wg.Wait() // blocks until Done() has been called once per Add()Picking the right tool
Worker pools bound concurrency for a stream of independent jobs. Fan-in/fan-out combine or split streams of work. WaitGroup answers "wait for these N things to finish". Context answers "stop everything if the caller no longer wants the result", which frequently gets combined with a WaitGroup: context cancels the work, WaitGroup confirms every goroutine actually noticed and exited.