Worker pool example where each result carries its own error, allowing all errors to be collected without canceling the remaining jobs.
package main
import (
"errors"
"fmt"
"sync"
)
type Result struct {
Worker int
Job int
Value int
Err error
}
func process(worker, job int) (int, error) {
fmt.Printf("worker %d processing job %d\n", worker, job)
if job%5 == 0 {
return 0, fmt.Errorf("job %d: divisible by 5", job)
}
return job * job, nil
}
func main() {
const numWorkers = 3
jobs := make(chan int)
results := make(chan Result)
var wg sync.WaitGroup
for w := range numWorkers {
wg.Add(1)
go func(id int) {
defer wg.Done()
for job := range jobs {
val, err := process(id, job)
results <- Result{Worker: id, Job: job, Value: val, Err: err}
}
}(w)
}
go func() {
for j := range 10 {
jobs <- j
}
close(jobs)
}()
go func() {
wg.Wait()
close(results)
}()
var errs []error
for r := range results {
if r.Err != nil {
errs = append(errs, r.Err)
continue
}
fmt.Printf("worker %d: job %2d -> %d\n", r.Worker, r.Job, r.Value)
}
if joined := errors.Join(errs...); joined != nil {
fmt.Println("\nerrs:")
fmt.Println(joined)
}
}