I saw the example of errgroup in godoc, and it makes me confused that it simply assigns the result to global results instead of using channels in each search routines. Heres the code:
Google := func(ctx context.Context, query string) ([]Result, error) {
g, ctx := errgroup.WithContext(ctx)
searches := []Search{Web, Image, Video}
results := make([]Result, len(searches))
for i, search := range searches {
i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines
g.Go(func() error {
result, err := search(ctx, query)
if err == nil {
results[i] = result
}
return err
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
I'm not sure is there any reason or implied rules guarantees it is correct? THX
答案 0 :(得分:1)
此处的目的是使searches
和results
一致。 Web
搜索的结果总是在results[0]
,Image
搜索的结果总是在results[1]
,依此类推。这也使一个简单的例子成为可能,因为存在无需使用其他消耗通道的goroutine。
如果goroutine将结果发送到通道中,则结果顺序将不可预测。如果可预测的结果顺序不是属性,那么您可以随意使用渠道。
答案 1 :(得分:1)
此代码中有秘密调味料会导致筒仓:
react-material
我们知道结果集的大小,因此在启动任何goroutine之前,我们都会预先分配所有插槽。这样可以消除对切片对象本身的争用
results := make([]Result, len(searches))
^^^^ ^^^^^^^^^^^^^
for i, search := ... {
i, search := i, search
^^^^^^^^^^
g.Go {
results[i] = result
^^^^^^^^^^
}
然后,我们将每次迭代的索引号和搜索属性提升为闭包,因此不会对循环/ goroutines使用的变量产生争用
make(.., len(searches))
^^^^ ^^^^^^^^^^^^^
最后,每个工作人员都在预定大小的切片中的单个插槽上操作:
i, search := i, search
保证工作人员仅对“结果”切片执行读取操作以找出其元素在何处( results[i] = result
。
此特定模式是有限制的,您必须在所有工作人员都完成后才能使用结果。因此,在决定是使用此流程还是基于渠道的管道工作流程时,请问问自己下一步将要做什么。
results[i]
如果对给定结果的分析独立于其他任何结果,则这是基于渠道的工作流程的不错选择。
但是,如果分析取决于顺序,或者结果彼此不同,那么您可能别无选择,只能序列化流程。