Implement a worker pool with result collection and proper shutdown.
Question 204HardGo 1.22 to 1.25
type Result struct {
Job int
Out string
Err error
}
func Pool(ctx context.Context, jobs []int, workers int) []Result {
jobCh := make(chan int)
resCh := make(chan Result)
var wg sync.WaitGroup
for range workers {
wg.Add(1)
go func() {
defer wg.Done()
for j := range jobCh {
out, err := work(ctx, j)
select {
case resCh <- Result{j, out, err}:
case <-ctx.Done():
return
}
}
}()
}
go func() { // producer: sole owner/closer of jobCh
defer close(jobCh)
for _, j := range jobs {
select {
case jobCh <- j:
case <-ctx.Done():
return
}
}
}()
go func() { wg.Wait(); close(resCh) }() // closer of resCh
var results []Result
for r := range resCh {
results = append(results, r)
}
return results
}
What the interviewer checks: (1) each channel has exactly one closer; (2) resCh is closed only after all workers finish, which needs the WaitGroup and a separate goroutine (calling wg.Wait() in the collector would deadlock on an unbuffered resCh); (3) every blocking send also watches ctx.Done(), so cancellation can't leak goroutines; (4) results arrive out of order, so include the job ID or index.
More on Channels & select
- Q202What does this print? (select evaluation order)
- Q203How would you implement a semaphore / bounded concurrency with channels?
- Q205What is a goroutine leak caused by channels? Show one and fix it.
- Q206Why is
chan struct{}preferred for signaling, and how doescloseact as a broadcast? - Q207What does this print? (len and cap of channels)
- Q208Channels or mutexes: how do you decide?