使用错误通道进行同步
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 循环在继续之前确保所有函数都已完成(有或无错误)。
- 使用错误进行同步很方便,因为它对于所有函数都是相同的类型。返回类型可能不同。如果我们需要在错误时取消,无论如何我们都需要从 goroutines 中恢复错误。
使用errgroup
正如@Зелёный 在 cmets 中所建议的,这里是一个使用(仍然)实验包 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
您也可以使用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 个错误变量。
- 你需要检查
wg.Wait之后的所有3个错误变量,这有点冗长。