首页 > 解决方案 > 如何尽早从循环内的 gouroutine 返回错误?

问题描述

我在一个循环中有一个 goroutine,我处理错误的方式是将它添加到一个通道中,并且在所有 goroutine 完成后,我检查是否有错误并相应地返回。

这样做的问题是我想在收到错误后立即返回错误,这样我就不会花时间等待所有 goroutine 完成,因为它效率低下。

我尝试添加select语句,但它不起作用,我无法select在 goroutines 中添加语句,因为我也想退出 for 循环和try函数。

我怎样才能做到这一点?

这是代码:

package main

import (
    "sync"
    "runtime"
    "fmt"
    "errors"
)

func try() (bool, error) {
    wg := new(sync.WaitGroup)

    s := []int{0,1,2,3,4,5}
    ec := make(chan error)
    
    for i, val := range s {
    /*
        select {
             case err, ok := <-ec:
        if ok {
            println("error 1", err.Error())
            return false, err
        }
            default:
            }
    */
        wg.Add(1)
        i := i
        val := val
        go func() {
            err := func(i int, val int, wg *sync.WaitGroup) error {
                defer wg.Done()
                
                if i == 3 {
                    return errors.New("one error")
                } else {
                    return nil
                }
                
            }(i, val, wg)
            if err != nil {
                ec <- err
                return
            }
        }()
    }
    wg.Wait()
    
    select {
    case err, ok := <-ec:
        if ok {
            println("error 2", err.Error())
            return false, err
        }
    default:
    }
    
    return true, nil
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    b, e := try()
    if e != nil {
        fmt.Println(e.Error(), b)
    } 
    
}

这是去游乐场链接

标签: goerror-handlingconcurrencychannelgoroutine

解决方案


wg.Wait()你的select声明之前,你实际上是在等待所有的 goroutine 返回。

问题是我想在收到错误后立即返回

我假设你的意思是只要其中任何一个返回错误就停止运行 goroutine。

在这种情况下,您可以使用context.Context来管理取消,但更好的是 an errgroup.Group,它很好地结合了上下文功能和同步:

包 errgroup 为处理公共任务的子任务的 goroutine 组提供同步、错误传播和上下文取消。

特别是Group.Go

第一次调用返回非 nil 错误会取消该组;Wait 会返回它的错误。

import (
    "sync"
    "runtime"
    "fmt"
    "errors"
    "golang.org/x/sync/errgroup"
)

func try() (bool, error) {
    errg := new(errgroup.Group)

    s := []int{0,1,2,3,4,5}
    
    for i, val := range s {       
        i := i
        val := val

        errg.Go(func() error {
            return func(i int, val int) error {
                if i == 3 {
                    return errors.New("one error")
                } else {
                    return nil
                }
            }(i, val)
        })
    }
    
    if err := errg.Wait(); err != nil {
        // handle error
    }
    
    return true, nil
}

https://play.golang.org/p/lSIIFJqXf0W


推荐阅读