Go

Implement a generic worker pool with a fixed number of workers, context cancellation, and no goroutine leaks.

Question 237HardGo 1.22 to 1.25
func WorkerPool[In, Out any](
	ctx context.Context,
	workers int,
	in <-chan In,
	fn func(context.Context, In) Out,
) <-chan Out {
	out := make(chan Out)
	var wg sync.WaitGroup
	for range workers {
		wg.Go(func() {
			for {
				select {
				case <-ctx.Done():
					return
				case v, ok := <-in:
					if !ok {
						return
					}
					select {
					case out <- fn(ctx, v):
					case <-ctx.Done():
						return
					}
				}
			}
		})
	}
	go func() {
		wg.Wait()
		close(out) // only after ALL workers have exited
	}()
	return out
}

Key points:

  • There are exactly workers goroutines, which bounds concurrency and memory.
  • Only the owner closes out. A separate goroutine closes it after wg.Wait(), so no worker ever sends on a closed channel.
  • Every blocking send and receive sits in a select with ctx.Done(), so an abandoned consumer never leaves workers blocked forever.
  • The producer that feeds in must also respect ctx and close in when it has no more work.

Results arrive out of order. To keep order, send (index, value) pairs or write into a pre-sized slice.

What the interviewer is looking for: who closes which channel, leak-free cancellation, and bounded goroutines. Also mention that for "N tasks, stop on first error" errgroup with SetLimit is simpler.

More on Concurrency Patterns & sync

All 38 Concurrency Patterns & sync questions