【问题标题】:Is errgroup in golang restrictive in its usage?golang 中的 errgroup 的使用有限制吗?
【发布时间】:2022-10-25 16:15:12
【问题描述】:

我正在学习 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 方法的所有函数调用都返回,然后从它们返回第一个非 nil 错误(如果有)。” 问题:

  1. 如果我想使用 errgroup 但想等到 所有 Go 方法共享的上下文被取消或全部 Go 方法的函数调用已经返回?
  2. 如果我想使用 errgroup 但想等到其中一个 Go 方法返回错误时,哪种方法会取消上下文而不是等待全部完成?

    不知何故,我觉得 errgroup 包的使用限制太大。我错过了什么?

【问题讨论】:

    标签: go


    【解决方案1】:

    似乎仅使用errGroup 本身是无法实现的。

    1. 也许waitGroup可以在这里使用。
    2. 只有在发生错误时才可以调用cancel。或者使用error 频道并等待第一个错误。

    【讨论】:

    • 似乎 errgroup 本身可以以不同的方式实现。它只报告执行的 Go 方法的一个错误。应该有一种机制来报告所有错误。毕竟它会等待所有 Go 方法完成执行。我怀疑 errgroup 本身是否有很大用处,但是对于 golang 的新手(像我一样)来说,缺少关于它如何与上下文一起使用的完整示例。
    【解决方案2】:

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

    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()
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-26
      • 2020-02-13
      • 1970-01-01
      • 2012-08-01
      • 2021-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多