为了账号安全,请及时绑定邮箱和手机立即绑定

第一次完成时如何安全地绕过其他 goroutine 的结果

第一次完成时如何安全地绕过其他 goroutine 的结果

Go
狐的传说 2021-11-22 10:45:50
我想向几台服务器询问数据(例如多个只读副本)。在这项任务中最重要的是速度,因此应该提供第一个结果,而其他所有结果都可以忽略。我对绕过这些数据的惯用方式有疑问。当它退出时,有这个问题的一切都没有问题(所有较慢的 goroutine 都没有完成它们的工作,因为主进程存在)。但是当我们取消最后一行的注释(使用 Sleep)时,我们可以看到其他 goroutines 也在做他们的工作。现在我正在通过通道推送数据有没有办法不推送它们?处理此类问题的好方法是什么?package mainimport (    "fmt"    "log"    "math/rand"    "time")type Result inttype Conn struct {    Id int}func (c *Conn) DoQuery(params string) Result {    log.Println("Querying start", params, c.Id)    time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)    log.Println("Querying end", params, c.Id)    return Result(1000 + c.Id*c.Id)}func Query(conns []Conn, query string) Result {    ch := make(chan Result)    for _, conn := range conns {        go func(c Conn) {            ch <- c.DoQuery(query)        }(conn)    }    return <-ch}func main() {    conns := []Conn{Conn{1}, Conn{2}, Conn{3}, Conn{4}, Conn{5}}    result := Query(conns, "query!")    fmt.Println(result)    // time.Sleep(time.Minute)}
查看完整描述

1 回答

?
千万里不及你

TA贡献1784条经验 获得超9个赞

我的建议是,使CH缓冲通道,每个查询一个空间:ch := make(chan Result, len(conns))。这样每个查询都可以运行完成,并且不会阻塞通道写入。


Query可以读取一次并返回第一个结果。当所有其他 goroutine 完成时,通道最终将被垃圾回收,一切都会消失。使用无缓冲通道,您可以创建许多永远不会终止的 goroutine。


编辑:如果你想取消飞行中的请求,它会变得更加困难。某些操作和 API 提供取消功能,而其他操作和 API 则不提供。对于 http 请求,您可以Cancel在请求结构上使用字段。只需提供一个您可以关闭以取消的频道:


func (c *Conn) DoQuery(params string, cancel chan struct{}) Result {

    //error handling omitted. It is important to handle errors properly. 

    req, _ := http.NewRequest(...)

    req.Cancel = cancel

    resp, _ := http.DefaultClient.Do(req)

    //On Cancellation, the request will return an error of some kind.

    return readData(resp)

}

func Query(conns []Conn, query string) Result {

    ch := make(chan Result)

    cancel := make(chan struct{})

    for _, conn := range conns {

        go func(c Conn) {

            ch <- c.DoQuery(query,cancel)

        }(conn)

    }


    first := <-ch

    close(cancel)

    return first

}

如果有一个您不关心的大型读取请求,这可能会有所帮助,但它实际上可能会或可能不会取消远程服务器上的请求。如果您的查询不是 http,而是数据库调用或其他什么,您将需要查看是否有类似的取消机制可以使用。


查看完整回答
反对 回复 2021-11-22
  • 1 回答
  • 0 关注
  • 171 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信