Channels, select & context
Worker pools, buffered and unbuffered channels, select with timeouts, and cancellation with context.
Go's motto is 'do not communicate by sharing memory; share memory by communicating'. Channels pass values between goroutines with built-in synchronization; select waits on several of them; context carries deadlines and cancellation through a program.
Patterns
Three workers read job IDs from jobs and send Readings to results; close(jobs) ends their range loops.
Results arrive in any order, so they are sorted before printing.
select picks whichever case is ready first: the 50 ms timer wins against the 200 ms reply.
context.WithTimeout produces a context whose Done channel closes after 30 ms, with Err() explaining why.
An unbuffered channel (or closing it) synchronizes two goroutines.
Sending on a channel nobody will ever receive from Make sure every send has a receiver, or use a buffered channel / select with a default or timeout
An unmatched send blocks forever; if all goroutines block, Go reports 'all goroutines are asleep - deadlock!'.
What is the difference between a buffered and an unbuffered channel?
An unbuffered channel's send waits until a receiver takes the value (a rendezvous). A buffered channel's send only waits when the buffer is full.