我想问几个服务器的数据(例如多个只读副本)。 在这项任务中,最重要的是速度,因此应该提供第一个结果 而其他所有可以被忽略。
我遇到绕过这些数据的惯用方法的问题。一切 当它退出时,这个问题是可以的(所有较慢的goroutine都没有 完成他们的工作,因为主要过程存在)。但是当我们取消评论时 最后一行(有睡眠)我们可以看到其他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)
}
答案 0 :(得分:5)
我的建议是使ch为每个查询一个空格的缓冲通道:ch := make(chan Result, len(conns))
。这样每个查询都可以运行完成,并且不会阻塞通道写入。
Query
可以读取一次并返回第一个结果。当所有其他goroutine完成后,该频道最终将被垃圾收集,一切都将消失。使用无缓冲的通道,您可以创建许多永远无法终止的goroutine。
编辑:
如果您想取消正在进行的请求,可能会变得更加困难。一些操作和apis提供取消,而其他操作和apis没有。使用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,而是数据库调用或其他内容,则需要查看是否存在可以使用的类似取消机制。