A strong answer
Three mechanics govern production multiplexing and cancellation in Go: (1) select Runtime Execution (runtime.selectgo): First, selectgo generates a pseudo-random permutation (pollorder) of all cases so no channel can starve another when multiple channels are ready simultaneously; second, it sorts all involved channel addresses (lockorder) to acquire their hchan.lock mutexes without AB/BA deadlock! (2) Timer Reuse (time.NewTimer vs time.After): Calling time.After(d) allocates a new time.Timer channel on the runtime heap on every single loop iteration (Prior to Go 1.23, unstopped timers were not even GC'd until their 5-minute deadline expired, and even in Go 1.23+ allocating 50,000 Timer structs/sec churns the heap!). Always allocate timer := time.NewTimer(d) once outside the loop and call timer.Reset(d)! (3) Context Trees & errgroup (golang.org/x/sync/errgroup): ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second) links a child node into the parent's cancellation tree; calling cancel() closes ctx.Done() (chan struct{}), broadcasting an immediate wakeup to every <-ctx.Done() listener! errgroup.WithContext(ctx) combines WaitGroup, error propagation, ctx cancellation on first error, and bounded concurrency (g.SetLimit(10)) in one primitive.
Closing a ctx.Done() channel wakes all goroutines blocked on <-ctx.Done() simultaneously in O(1) broadcast semantics; inside errgroup.WithContext(ctx), the first goroutine returning a non-nil error cancels the derived ctx and g.Wait() waits for all sibling goroutines to exit before returning that error. Because receiving from a closed channel (<-ctx.Done()) returns immediately without consuming a token, a single close(c.done) inside context.cancelCtx unblocks an arbitrary number $N$ of waiting goroutines concurrently.
Replacing time.After inside a 50k msg/sec loop with a single reusable time.NewTimer + .Reset() reduces timer heap allocations from 50,000/sec down to 1, eliminating megabytes of GC churn per second. In production systems (Go runtime/select.go (selectgo), context package (WithCancel, WithTimeout, WithoutCancel, AfterFunc), and golang.org/x/sync/errgroup (SetLimit, WithContext).), engineers monitor these exact trade-offs to keep p99 latency predictable and prevent resource exhaustion under peak load.
What the interviewer is listening for
- Traces how
selectmultiplexing,context.Contextcancellation trees, anderrgroup.Groupoperates in memory step by step rather than giving a vague textbook definition. - States the core correctness invariant and explains why edge cases do not violate it.
- Quantifies time, space, and hardware costs (
close(ctx.Done())Broadcast,time.After(d)Inside Hot Loop) with concrete numbers. - Identifies real-world failure modes (Shadowing
ctxwithg, ctx := errgroup.WithContext(ctx)and reusingctxafterg.Wait()returns!) and explains how production systems mitigate them.
Common mistakes
- Treating
selectmultiplexing,context.Contextcancellation trees, anderrgroup.Groupas a black box without explaining the underlying memory or state transitions. - Ignoring worst-case tail latency or memory overhead (Forgetting
defer cancel()afterctx, cancel := context.WithTimeout(parent, 5*time.Second)). - Forgetting boundary conditions and error recovery (Putting
case <-time.After(timeout):inside a high-throughputfor { select { ... } }loop).