为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Go API 中并发运行函数,而不是顺序运行函数?

如何在 Go API 中并发运行函数,而不是顺序运行函数?

Go
料青山看我应如是 2022-08-01 11:07:51
在API中,如果我们需要查询多个表,我们如何同时实现它,而不是遵循顺序方式,即func sampleAPI(w http.ResponseWriter, r *http.Request) {    a, err := getFromATable();  //1    if err != nil {       w.WriteHeader(http.StatusInternalServerError)       return    }    b, err := getFromBTable();  //2    if err != nil {       w.WriteHeader(http.StatusInternalServerError)       return    }    c, err := getFromCTable();  //3    if err != nil {       w.WriteHeader(http.StatusInternalServerError)       return    }    .    .    .}我想同时调用上述函数1,2,3。我怎样才能做到这一点
查看完整描述

3 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

使用错误通道进行同步

func sampleAPI(w http.ResponseWriter, r *http.Request) {

    chErr := make(chan error)


    var a correctType

    go func() {

        var err error

        a, err = getFromATable()

        chErr <- err

    }()


    var b correctType

    go func() {

        var err error

        b, err = getFromBTable()

        chErr <- err

    }()


    var c correctType

    go func() {

        var err error

        c, err = getFromCTable()

        chErr <- err

    }()


    var err error

    for i := 0; i < 3; i++ {

        if r := <-chErr; r != nil {

            err = r

        }

    }

    if err != nil {

        w.WriteHeader(http.StatusInternalServerError)

        // etc.

        return

    }


    // continue to do stuff with a, b, c

}

一些注意事项:

  • 每个 go 函数都必须向 添加一个值。确保它!如果没有错误,则写入(如果没有错误,此示例将写入)。chErrnilchErr

  • for 循环必须迭代与启动 go 函数相同的数量。

  • for 循环确保所有函数都已完成(有或没有错误),然后再继续。

  • 使用错误进行同步很方便,因为它对于所有函数都是相同的类型。返回类型可能不同。如果我们需要在错误时取消,我们无论如何都需要将错误从goroutines中取回。

使用errgroup

正如 @Зелёный 在注释中所建议的那样,这里有一个使用(仍然)实验包错误组的示例

func sampleAPI(w http.ResponseWriter, r *http.Request) {

    g, ctx := errgroup.WithContext(context.TODO())


    var a correctType

    g.Go(func() (err error) {

        a, err = getFromATable(ctx)

        return err

    })


    var b correctType

    g.Go(func() (err error) {

        b, err = getFromBTable(ctx)

        return err

    })


    var c correctType

    g.Go(func() (err error) {

        c, err = getFromCTable(ctx)

        return err

    })


    if err := g.Wait(); err != nil {

        w.WriteHeader(http.StatusInternalServerError)

        // etc.

        return

    }


    // continue to do stuff with a, b, c

}

一些注意事项:

  • 这个检查所有错误,并为您返回第一个错误。

  • 如果一个错误,它还会取消剩余的调用(因此ctx)

  • 它使用sync.WaitGroup

  • 缺点:它是一个额外的依赖项,因为它不是标准库的一部分(尚未)。

使用WaitGroup

还可以使用 a 来等待所有函数都返回其结果。sync.WaitGroup

func sampleAPI(w http.ResponseWriter, r *http.Request) {

    var wg sync.WaitGroup

    wg.Add(3)


    var a correctType

    var errA error

    go func() {

        defer wg.Done()

        a, errA = getFromATable()

    }()


    var b correctType

    var errB error

    go func() {

        defer wg.Done()

        b, errB = getFromBTable()

    }()


    var c correctType

    var errC error

    go func() {

        defer wg.Done()

        c, errC = getFromCTable()

    }()


    wg.Wait()

    

    if errA != nil {

        w.WriteHeader(http.StatusInternalServerError)

        // etc.

        return

    }

    if errB != nil {

        w.WriteHeader(http.StatusInternalServerError)

        // etc.

        return

    }

    if errC != nil {

        w.WriteHeader(http.StatusInternalServerError)

        // etc.

        return

    }


    // continue to do stuff with a, b, c

}

一些注意事项:

  • 这里需要 3 个错误变量。

  • 您需要在 之后检查所有3个错误变量,使其有点冗长。wg.Wait



查看完整回答
反对 回复 2022-08-01
?
慕码人8056858

TA贡献1803条经验 获得超6个赞

您还可以使用等待组来等待所有 api 调用完成,然后再继续操作:


func sampleAPI(w http.ResponseWriter, r *http.Request) {

    var res1, res2, res3 myCustomType

    var err1, err2, err2 error


    var wg sync.WaitGroup

        wg.Add(3)


    go func() {

        defer wg.Done()

        res1, err1 = getFromATable();  //1

    }()


    go func() {

        defer wg.Done()

        res2, err2 = getFromBTable();  //2

    }()


    go func() {

        defer wg.Done()

        res3, err3 = getFromXTable();  //3

    }()


    wg.Wait()

}

进一步参考也 https://gobyexample.com/waitgroups 和 https://tutorialedge.net/golang/go-waitgroup-tutorial/


查看完整回答
反对 回复 2022-08-01
?
慕妹3146593

TA贡献1820条经验 获得超9个赞

如果 a、b 和 c 是不同的类型,则可以尝试以下方法。interface{}类型只是为了展示如何编码,你可以根据自己的要求修改通道类型。


    aCh := make(chan interface{})

    bCh := make(chan interface{})

    cCh := make(chan interface{})

    defer close(aCh)

    defer close(bCh)

    defer close(cCh)

    

    go func() {

      a, _ := GetFromATable()

      aCh <- a

    }()

    go func() {

      b, _ := GetFromBTable()

      bCh <- b

    }()

    go func() {

      c, _ := GetFromCTable()

      cCh <- c

    }()


    fmt.Println(<- aCh, <- bCh, <- cCh)

如果所有三个输出都处于同一类型,则只需使用单个通道或使用带有 select 语句的 forever for 循环来处理流式处理要求。


查看完整回答
反对 回复 2022-08-01
  • 3 回答
  • 0 关注
  • 103 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信