Worker Pool Bounded Goroutines

Process a job channel with a fixed pool of workers, draining remaining jobs on shutdown and collecting per-worker stats safely.

GoResilience

Code

snippet
package main

import (
	"context"
	"fmt"
	"sync"
)

type Job struct {
	ID  int
	URL string
}

func runPool(ctx context.Context, jobs []Job, workers int) (map[int]int, error) {
	jobCh := make(chan Job)
	stats := make([]int, workers)
	var wg sync.WaitGroup

	for w := 0; w < workers; w++ {
		w := w
		wg.Add(1)
		go func() {
			defer wg.Done()
			for job := range jobCh {
				select {
				case <-ctx.Done():
					return // 优雅退出:剩余任务由调用方决定去留
				default:
					fmt.Println("worker", w, "handles", job.ID)
					stats[w]++
				}
			}
		}()
	}

	dispatchDone := make(chan struct{})
	go func() {
		defer close(dispatchDone)
		for _, job := range jobs {
			select {
			case jobCh <- job:
			case <-ctx.Done():
				return
			}
		}
	}()

	<-dispatchDone
	close(jobCh) // 关闭通道即全体工人的退出信号
	wg.Wait()

	perWorker := make(map[int]int, workers)
	for w, count := range stats {
		perWorker[w] = count
	}
	return perWorker, ctx.Err()
}

func main() {
	jobs := make([]Job, 12)
	for i := range jobs {
		jobs[i] = Job{ID: i, URL: fmt.Sprintf("https://example.com/%d", i)}
	}
	stats, err := runPool(context.Background(), jobs, 4)
	fmt.Println(stats, err)
}