有时候,一份数据可能同时从多个数据源获取。这些数据源将返回相同的数据。因为各种因素,这些数据源的回应速度参差不一,甚至某个特定数据源的多次回应速度之间也可能相差很大。
同时从多个数据源获取一份相同的数据可以有效保障低延迟。我们只需采用最快的回应并舍弃其它较慢回应。
e.g.
注意:如果有 N 个数据源,为了防止被舍弃的回应对应的协程永久阻塞,则传输数据用的通道必须为一个容量至少为 N-1 的缓冲通道。
func source(c chan<- int32) {
ra, rb := rand.Int31(), rand.Intn(3)+1
time.Sleep(time.Duration(rb) * time.Second)
c <- ra
}
func main() {
rand.Seed(time.Now().UnixNano())
c := make(chan int32, 5) // 必须用一个缓冲通道
for i := 0; i < cap(c); i++ {
go source(c)
}
rnd := <-c // 只有第一个回应被使用了
fmt.Println(rnd)
}
e.g.
也可以使用选择机制来实现“采用最快回应”用例。 每个数据源协程只需使用一个缓冲为1的通道并向其尝试发送回应数据即可。
func source(c chan<- int32) {
ra, rb := rand.Int31(), rand.Intn(3)+1
time.Sleep(time.Duration(rb) * time.Second)
select {
case c <- ra:
default:
}
}
func main() {
rand.Seed(time.Now().UnixNano())
c := make(chan int32, 1) // 此通道容量必须至少为1
for i := 0; i < 5; i++ {
go source(c)
}
rnd := <-c // 只采用第一个成功发送的回应数据
fmt.Println(rnd)
}
e.g.
如果一个“采用最快回应”用例中的数据源的数量很少,比如两个或三个,我们可以让每个数据源使用一个单独的缓冲通道来回应数据,然后使用一个 select 代码块来同时接收这三个通道。
func source() <-chan int32 {
c := make(chan int32, 1) // 必须为一个缓冲通道
go func() {
ra, rb := rand.Int31(), rand.Intn(3)+1
time.Sleep(time.Duration(rb) * time.Second)
c <- ra
}()
return c
}
func main() {
rand.Seed(time.Now().UnixNano())
var rnd int32
select{
case rnd = <-source():
case rnd = <-source():
case rnd = <-source():
}
fmt.Println(rnd)
}
e.g.
如果需要处理许多个chan 呢?或者是,chan 的数量在编译的时候是不定的,在运行的时候需要处理一个 slice of chan,这个时候,通过 reflect.Select 函数,你可以将一组运行时的 case 传入,当作参数执行。
Go 的 select 是伪随机的,它可以在执行的 case 中随机选择一个 case,并把选择的这个 case 的索引(chosen)返回,如果没有可用的 case 返回,会返回一个 bool 类型的返回值,这个返回值用来表示是否有 case 成功被选择。如果是 recv case,还会返回接收的元素。Select 的方法签名如下:
// Select executes a select operation described by the list of cases.
// Like the Go select statement, it blocks until at least one of the cases
// can proceed, makes a uniform pseudo-random choice,
// and then executes that case. It returns the index of the chosen case
// and, if that case was a receive operation, the value received and a
// boolean indicating whether the value corresponds to a send on the channel
// (as opposed to a zero value received because the channel is closed).
// Select supports a maximum of 65536 cases.
func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
func source() <-chan int32 {
c := make(chan int32, 1) // 必须为一个缓冲通道
go func() {
ra, rb := rand.Int31(), rand.Intn(3)+1
time.Sleep(time.Duration(rb) * time.Second)
c <- ra
}()
return c
}
func quickReplyReflect(chs ...<-chan int32) <-chan int32 {
out := make(chan int32)
go func() {
defer close(out)
// 构造SelectCase slice
var cases []reflect.SelectCase
for _, c := range chs {
cases = append(cases, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(c),
})
}
// 循环,从cases中选择一个可用的
for len(cases) > 0 {
i, v, ok := reflect.Select(cases)
if !ok { // 此通道关闭并且它的缓冲队列中为空
cases = append(cases[:i], cases[i+1:]...)
continue
}
out <- int32(v.Int())
return // 该例子中只取一个
}
}()
return out
}
func main() {
rand.Seed(time.Now().UnixNano())
rnd := <-orReflect(source(), source(), source())
fmt.Println(rnd)
}