本文介绍了如何在Go API中并发运行函数而不是顺序运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在API中,如果我们需要查询多个表,我们如何才能并发实现它,而不是遵循顺序方式,即

In an API, if we need to query multiple tables how can we achieve it concurrently instead of following the sequential way,i.e

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函数.我该如何实现

I want to call the above functions 1,2,3 concurrently. How can I achieve that

推荐答案

使用错误通道进行同步

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函数必须向 chErr 添加一个值.确保它!如果没有错误,请将 nil 写入 chErr (如果没有错误,本示例将执行此操作).
  • for循环必须迭代与启动go函数相同的数量.
  • for循环可确保所有功能都已完成(有或没有错误),然后再继续.
  • 使用错误进行同步很方便,因为所有功能的错误类型相同.返回类型可能不同.如果我们需要取消错误,则无论如何都需要从goroutine中恢复错误.
  • Every go function must add a value to chErr. Make sure of it! If there is no error, write nil to chErr (which this example does if there is no error).
  • The for loop must iterate the same amount as there were go functions started.
  • The for loop makes sure all functions have completed (with or without error) before continuing.
  • Using the error to synchronise is convenient as it is the same type for all functions. The return type might be different. If we need to cancel on error, we need to get the error back out of the goroutines anyway.

如@Зелёный在评论中所建议,此处是使用(仍)实验包 errgroup :

As suggested by @Зелёный in the comments, here an example using the (still) experimental package 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
  • 缺点:这是一个额外的依赖项,因为它尚未成为标准库的一部分.

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

You can also use a sync.WaitGroup to wait until all functions have returned their results.

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个错误变量.
  • 您需要在 wg.Wait 之后检查所有3个错误变量,使其显得有些冗长.
  • You need 3 error variables here.
  • You need to check all 3 error variables after wg.Wait, making it a bit verbose.

这篇关于如何在Go API中并发运行函数而不是顺序运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 04:46