Patterns for Bounded Concurrency with Context in Go

Limit concurrent goroutines in Go using a buffered channel semaphore and context for cancellation.

Use a sync.WaitGroup combined with a buffered channel to limit concurrent goroutines, or use context.WithCancel to propagate cancellation signals. The buffered channel acts as a semaphore to enforce the concurrency limit, while the context ensures all workers stop when the parent operation completes.

func boundedWork(ctx context.Context, jobs []Job, maxWorkers int) {
    var wg sync.WaitGroup
    sem := make(chan struct{}, maxWorkers)
    
    for _, job := range jobs {
        select {
        case <-ctx.Done():
            return
        default:
        }
        
        sem <- struct{}{}
        wg.Add(1)
        go func(j Job) {
            defer wg.Done()
            defer func() { <-sem }()
            
            if err := doWork(ctx, j); err != nil {
                log.Println(err)
            }
        }(job)
    }
    wg.Wait()
}