如何在循环嵌套的Goroutine中正确收集错误并管理WaitGroup?
正确实现Goroutine循环中的错误收集与WaitGroup管理
原代码存在的核心问题
- WaitGroup计数错误:外层循环中多余的
wg.Add(1)导致WaitGroup计数比实际启动的Goroutine多,最终wg.Wait()会永久阻塞。 - 错误通道死锁:无缓冲通道的发送操作会阻塞直到有接收方,但原代码先执行
wg.Wait(),所有Goroutine卡在发送错误的步骤无法完成,进而触发死锁。 - 仅收集单个错误:只从通道接收一次错误,无法获取所有Goroutine产生的异常信息。
修正后的实现方案
package main import ( "fmt" "math/rand" "sync" "time" ) func init() { rand.Seed(time.Now().UnixNano()) } func worker(n int, wg *sync.WaitGroup, errs chan<- error) { defer wg.Done() fmt.Println("num ", n) if n == 1 { errs <- fmt.Errorf("错误来自num为%d的worker", n) } } func main() { var wg sync.WaitGroup platforms := 2 types := 3 totalWorkers := platforms * types * 2 // 创建缓冲通道,大小等于Goroutine总数,避免发送阻塞 errs := make(chan error, totalWorkers) wg.Add(totalWorkers) for j := 0; j < platforms; j++ { for k := 0; k < types; k++ { n := rand.Intn(2) go worker(n, &wg, errs) } for k := 0; k < types; k++ { n := rand.Intn(2) go worker(n, &wg, errs) } } // 单独启动Goroutine,等待所有工作完成后关闭错误通道 go func() { wg.Wait() close(errs) }() // 收集所有非空错误 var errorList []error for err := range errs { if err != nil { errorList = append(errorList, err) } } // 输出结果 if len(errorList) > 0 { fmt.Printf("共收集到%d个错误:\n", len(errorList)) for _, err := range errorList { fmt.Println("-", err) } } else { fmt.Println("未发现任何错误") } }
关键优化点说明
- WaitGroup精准计数:通过计算总Goroutine数量,一次性调用
wg.Add(totalWorkers),确保计数与实际启动的Goroutine完全匹配,避免手动循环Add时的遗漏或多余操作。 - 缓冲错误通道:通道缓冲大小设为Goroutine总数,保证每个worker发送错误时不会阻塞,无需等待接收方立即处理。
- 安全关闭通道:单独启动一个Goroutine等待所有工作完成后关闭错误通道,这样
main函数中的range errs可以正常遍历所有错误,直到通道关闭后退出循环。 - 完整错误收集:通过
range遍历通道,收集所有非空错误,确保不会遗漏任何Goroutine产生的异常。
内容的提问来源于stack exchange,提问作者SaneQ
相关产品推荐
相关产品推荐

