package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, id int, results chan<- int) { defer fmt.Printf("Worker %d finished ", id) // Simulate work that might take some time for { select { case <-ctx.Done(): return // Context cancelled, stop working case <-time.After(time.Duration(id+1) * 100 * time.Millisecond): // Simulate producing a result select { case <-ctx.Done(): return // Context cancelled while waiting to send case results <- id * 10: return // Sent result, worker is done for this task } } } } func main() { const numWorkers = 5 const numResultsToCollect = 3 results := make(chan int) ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup // Start workers for i := 0; i < numWorkers; i++ { wg.Add(1) go func(workerID int) { defer wg.Done() worker(ctx, workerID, results) }(i) } collected := 0 var finalResults []int // Collect results go func() { for result := range results { finalResults = append(finalResults, result) collected++ fmt.Printf("Collected result: %d (total: %d) ", result, collected) if collected >= numResultsToCollect { cancel() // We have enough, cancel other workers break } } // Drain any remaining messages if the channel isn't closed by the sender (not the case here) // In this specific setup, the `results` channel will be implicitly closed by the sender only after all workers are done. // For a more robust solution, the collecting goroutine would also need to know when all producers are done. }() wg.Wait() // Wait for all workers to complete (or be cancelled) // In a real application, you might need a mechanism to explicitly close `results` channel // or ensure the collector goroutine exits cleanly after `wg.Wait()` and `cancel()`. // For this example, a small sleep ensures output consistency. time.Sleep(10 * time.Millisecond) fmt.Println(" Final Collected Results:", finalResults) }