场景:需要访问Mysql批量查询多个用户的信息。由于需要使用 id in (*)这种语法,id数量很多时要多线程并发操作。常规模式:
func query(ctx context.Context, id []int, ch chan T) {
res := T
sql := "select * from user where id in (***)"
data, err := client.Query(ctx, sql)
if err != nil {
res.Err = err
ch <- res
return
}
res.Value = data
ch <- res
return
}
func BatchQuery(ctx context.Context, id []int) {
n := len(id)/perBatch
ch := make(chan T, n)
defer close(ch)
for i := 0; i < n; i++ {
go query(ctx, id[i*perBatch, (i+1)*perBatch])
}
recvCnt := 0
timeout := time.After(3*second)
select {
if recvCnt == n {
break
}
case value := <-ch:
recvCnt++
if value.Err != nil {
break
}
// ...
case <- timeout:
recvCnt = n
}
}
这里的问题是子协程的错误信息难以传递给主协程,而且如果在select中break,返回异常,其他协程再往channel里写数据会panic。 errgroup可以解决这个问题。