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

我想向几台服务器询问数据(例如多个只读副本)。在这项任务中最重要的是速度,因此应该提供第一个结果,而其他所有结果都可以忽略。


我对绕过这些数据的惯用方式有疑问。当它退出时,有这个问题的一切都没有问题(所有较慢的 goroutine 都没有完成它们的工作,因为主进程存在)。但是当我们取消最后一行的注释(使用 Sleep)时,我们可以看到其他 goroutines 也在做他们的工作。


现在我正在通过通道推送数据有没有办法不推送它们?


处理此类问题的好方法是什么?


package main


import (

    "fmt"

    "log"

    "math/rand"

    "time"

)


type Result int


type 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)

}


狐的传说
浏览 181回答 1
1回答

千万里不及你

我的建议是,使CH缓冲通道,每个查询一个空间:ch := make(chan Result, len(conns))。这样每个查询都可以运行完成,并且不会阻塞通道写入。Query可以读取一次并返回第一个结果。当所有其他 goroutine 完成时,通道最终将被垃圾回收,一切都会消失。使用无缓冲通道,您可以创建许多永远不会终止的 goroutine。编辑:如果你想取消飞行中的请求,它会变得更加困难。某些操作和 API 提供取消功能,而其他操作和 API 则不提供。对于 http 请求,您可以Cancel在请求结构上使用字段。只需提供一个您可以关闭以取消的频道:func (c *Conn) DoQuery(params string, cancel chan struct{}) Result {&nbsp; &nbsp; //error handling omitted. It is important to handle errors properly.&nbsp;&nbsp; &nbsp; req, _ := http.NewRequest(...)&nbsp; &nbsp; req.Cancel = cancel&nbsp; &nbsp; resp, _ := http.DefaultClient.Do(req)&nbsp; &nbsp; //On Cancellation, the request will return an error of some kind.&nbsp; &nbsp; return readData(resp)}func Query(conns []Conn, query string) Result {&nbsp; &nbsp; ch := make(chan Result)&nbsp; &nbsp; cancel := make(chan struct{})&nbsp; &nbsp; for _, conn := range conns {&nbsp; &nbsp; &nbsp; &nbsp; go func(c Conn) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ch <- c.DoQuery(query,cancel)&nbsp; &nbsp; &nbsp; &nbsp; }(conn)&nbsp; &nbsp; }&nbsp; &nbsp; first := <-ch&nbsp; &nbsp; close(cancel)&nbsp; &nbsp; return first}如果有一个您不关心的大型读取请求,这可能会有所帮助,但它实际上可能会或可能不会取消远程服务器上的请求。如果您的查询不是 http,而是数据库调用或其他什么,您将需要查看是否有类似的取消机制可以使用。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go