golang errgroup使用

134 阅读1分钟

场景:需要访问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可以解决这个问题。