Golang中如何处理goroutine的错误

Golang中如何处理goroutine的错误 在我的代码中,有一个函数用于从数据库获取不同ID的数据,因此我为每个ID使用了Go协程。但如果任何协程遇到错误,该如何处理?我卡在这里了。

以下是我的代码片段:

func  BStat(ID string) (t []string, err error) {

    var wg sync.WaitGroup
    var e error

    ch1 := make(chan string)
    ch2 := make(chan error)
    defer close(ch1)
    defer close(ch2)


    for i:=0 ; i <10 :i++  {
        wg.Add(1)
        go GetStat(ID, ch1, ch2 , &wg)
    }

    for i:=0 ; i <10 :i++{
        e = <- ch2
        if e != nil {                                               // 如何正确处理错误
            fmt.Println("Heloooooooooooooooo")
            break
            //return nil, errors.New("error in generating report ")
        }
        
        Stat, ok := <-ch1
        if ok == true {
                t = append(t, Stat)
            }
        }

    wg.Done()

    return t, e
}
func GetStat( Id uint64, ch1 chan<- []string , ch2 chan<- bool , wg *sync.WaitGroup) {

    stat, err := store.GetName(db, i)    // 从数据库获取值
    if err != nil {
                logger.WithError(err)
                ch2 <- true
                ch1 <- ""
    }
    ch2 <- false
    ch1 <- stat
}

我的Go协程卡住了,请建议我如何处理输出以及错误情况。

提前感谢


更多关于Golang中如何处理goroutine的错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

谢谢Ryze,我会研究一下的

更多关于Golang中如何处理goroutine的错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我很难理解这段代码。我一开始把它放到了Go Playground里,但它无法构建:https://play.golang.org/p/mYwthC8_qxj

根据你调用GetStat时传入的参数(而不是GetStat接受的参数),看起来GetStat应该从ch1读取输入,并将其接收到的任何错误写入ch2。

在你的代码中,处理goroutine错误的主要问题在于错误处理逻辑和通道使用方式。以下是改进后的代码示例:

func BStat(ID string) ([]string, error) {
    var wg sync.WaitGroup
    results := make([]string, 0, 10)
    errCh := make(chan error, 10) // 缓冲通道避免阻塞
    resultCh := make(chan string, 10)
    
    // 启动goroutines
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(index int) {
            defer wg.Done()
            stat, err := GetStat(ID, index)
            if err != nil {
                errCh <- err
                return
            }
            resultCh <- stat
        }(i)
    }
    
    // 等待所有goroutine完成
    go func() {
        wg.Wait()
        close(resultCh)
        close(errCh)
    }()
    
    // 收集结果和错误
    var firstErr error
    resultCount := 0
    
    for resultCount < 10 {
        select {
        case result, ok := <-resultCh:
            if ok {
                results = append(results, result)
                resultCount++
            }
        case err, ok := <-errCh:
            if ok && firstErr == nil {
                firstErr = err
            }
        }
    }
    
    return results, firstErr
}

func GetStat(ID string, index int) (string, error) {
    // 模拟数据库查询
    stat, err := store.GetName(db, ID, index)
    if err != nil {
        logger.WithError(err).Error("failed to get stat")
        return "", err
    }
    return stat, nil
}

或者使用errgroup.Group(如果你使用Go 1.20+):

import "golang.org/x/sync/errgroup"

func BStat(ID string) ([]string, error) {
    g, ctx := errgroup.WithContext(context.Background())
    results := make([]string, 0, 10)
    var mu sync.Mutex
    
    for i := 0; i < 10; i++ {
        i := i // 创建局部变量
        g.Go(func() error {
            select {
            case <-ctx.Done():
                return ctx.Err()
            default:
                stat, err := GetStat(ID, i)
                if err != nil {
                    return err
                }
                mu.Lock()
                results = append(results, stat)
                mu.Unlock()
                return nil
            }
        })
    }
    
    if err := g.Wait(); err != nil {
        return results, err
    }
    return results, nil
}

对于你的原始代码,主要问题:

  1. 使用了未缓冲的通道,可能导致goroutine阻塞
  2. 错误处理逻辑不完整,break只退出循环但不停止其他goroutine
  3. wg.Done()调用位置错误

改进后的代码确保:

  • 使用缓冲通道避免死锁
  • 正确收集所有结果和第一个错误
  • 所有goroutine都能正常完成或返回错误
回到顶部