You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在循环嵌套的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:35:21