golang中的errgroup在使用上有限制吗?

mfuanj7w  于 2022-12-07  发布在  Go
关注(0)|答案(2)|浏览(148)

我正在学习golang,在上下文环境中使用errgroup包时感到困惑。下面是我的简单代码:

package main
  
  import (
    "context"
    "errors"
    "fmt"
    "time"
  
    "golang.org/x/sync/errgroup"
  )
  
  func main() {
    fmt.Println("..................")
    ctx := context.Background()
    group, ctx := errgroup.WithContext(ctx)
    ctx, cancel := context.WithCancel(ctx)
  
    group.Go(func() error {
        //return errors.New("Error 1")
        time.Sleep(8 * time.Second)
        fmt.Println("Sleep 1 ended..................")
        cancel()
        return errors.New("Error 1")
    })
  
    group.Go(func() error {
        //return errors.New("Error 1")
        time.Sleep(2 * time.Second)
        fmt.Println("Sleep 2 ended..................")
        cancel()
        return errors.New("Error 2")
    })
  
    err := group.Wait()
  
    if err != nil {
        fmt.Println("Error: ", err)
    }
    fmt.Println("..................")
  
  }

输出结果如预期:

..................
   Sleep 2 ended..................
   Sleep 1 ended..................
   Error:  Error 2
   ..................

group.wait()“阻塞,直到Go方法的所有函数调用都返回,然后返回它们的第一个非空错误(如果有的话)。”
1.如果我想使用errgroup,但又想等到所有Go方法共享的上下文被取消,或者Go方法的所有函数调用都返回之后,该怎么办?
1.如果我想使用errgroup,但又想等到Go方法返回错误,那么哪个方法会取消上下文,而不是等待所有操作完成呢?
不知何故,我觉得errgroup包在使用上限制太多了。我遗漏了什么?

yfwxisqw

yfwxisqw1#

这似乎不能仅仅通过使用errGroup本身来实现。
1.也许这里可以使用waitGroup
1.可能只有在发生错误时才调用cancel。或者使用error通道并等待直到出现第一个错误。

yrefmtwq

yrefmtwq2#

此实现回答了您的两个问题:

package main

import (
    "context"
    "errors"
    "fmt"
    "time"

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

func main() {
    ctx := context.Background()

    g, gtx := errgroup.WithContext(ctx)

    g.Go(func() error {
        select {
        case <-time.After(8 * time.Second):
            fmt.Println("Sleep 1 ended..................")
            return errors.New("Error 1")
        case <-gtx.Done():
            return gtx.Err()
        }
    })

    g.Go(func() error {
        select {
        case <-time.After(2 * time.Second):
            fmt.Println("Sleep 2 ended..................")
            return errors.New("Error 2")
        case <-gtx.Done():
            return gtx.Err()
        }
    })

    err := g.Wait()
    if err != nil {
        fmt.Println("Error: ", err)
    }
    fmt.Println("..................")
}

它将打印以下内容:

..................
   Sleep 2 ended..................
   Error:  Error 2
   ..................

返回一个错误会隐式地取消上下文,你需要等待ctx.Done()显式地放弃go例程的执行。
使用time.Sleep可能被认为是一种反模式,因为它不确认上下文取消用途:

select {
    case <-time.After(x):
    case <-ctx.Done():
        return ctx.Err()
}

相关问题