英文:
how to select for input on a dynamic list of channels in Go?
问题
Go有一种机制可以从多个通道中进行阻塞读取,即select语句。因此,你可以这样说:
select {
case <- c1:
case <- c2:
}
将会阻塞,直到从这两个通道中的任意一个接收到输入。非常好。
但是,这要求我在源代码中指定我要轮询的通道数量。如果我有一个通道的切片或数组,并且我希望在任何一个通道上接收到输入之前进行阻塞,该怎么办?
英文:
Go has a mechanism to do a blocking read from one of several channels, the select statement. So you can say
select {
case <- c1:
case <- c2:
}
will block until we get input from either of these two channels. Very nice.
But this requires that I specify in the source code how many channels I want to poll. What if I have a slice or array of channels and I want to block until I get input on any of them?
答案1
得分: 8
自从go1.1版本以来,有一个适当的API可以动态地进行选择集操作。
下面是一个完整且可用的示例:
package main
import (
"log"
"reflect"
)
func sendToAny(ob int, chs []chan int) int {
set := []reflect.SelectCase{}
for _, ch := range chs {
set = append(set, reflect.SelectCase{
Dir: reflect.SelectSend,
Chan: reflect.ValueOf(ch),
Send: reflect.ValueOf(ob),
})
}
to, _, _ := reflect.Select(set)
return to
}
func recvFromAny(chs []chan int) (val int, from int) {
set := []reflect.SelectCase{}
for _, ch := range chs {
set = append(set, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
})
}
from, valValue, _ := reflect.Select(set)
val = valValue.Interface().(int)
return
}
func main() {
channels := []chan int{}
for i := 0; i < 5; i++ {
channels = append(channels, make(chan int))
}
go func() {
for i := 0; i < 10; i++ {
x := sendToAny(i, channels)
log.Printf("Sent %v to ch%v", i, x)
}
}()
for i := 0; i < 10; i++ {
v, x := recvFromAny(channels)
log.Printf("Received %v from ch%v", v, x)
}
}
您可以在playground上进行交互式操作。
英文:
Since go1.1, there's a proper API to dynamically do select sets.
Here's a complete and usable example:
package main
import (
"log"
"reflect"
)
func sendToAny(ob int, chs []chan int) int {
set := []reflect.SelectCase{}
for _, ch := range chs {
set = append(set, reflect.SelectCase{
Dir: reflect.SelectSend,
Chan: reflect.ValueOf(ch),
Send: reflect.ValueOf(ob),
})
}
to, _, _ := reflect.Select(set)
return to
}
func recvFromAny(chs []chan int) (val int, from int) {
set := []reflect.SelectCase{}
for _, ch := range chs {
set = append(set, reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
})
}
from, valValue, _ := reflect.Select(set)
val = valValue.Interface().(int)
return
}
func main() {
channels := []chan int{}
for i := 0; i < 5; i++ {
channels = append(channels, make(chan int))
}
go func() {
for i := 0; i < 10; i++ {
x := sendToAny(i, channels)
log.Printf("Sent %v to ch%v", i, x)
}
}()
for i := 0; i < 10; i++ {
v, x := recvFromAny(channels)
log.Printf("Received %v from ch%v", v, x)
}
}
You can play around with it interactively on the playground
答案2
得分: 5
只是一个想法,但你可以使用多路复用模式,其中你可以生成一个带有2个通道的goroutine,它会在这两个通道上阻塞并将输出发送到一个新的通道。然后你可以动态地从列表中构建这些通道的树状结构,将所有内容汇聚到一个单一的通道中,然后在该通道上进行读取。
英文:
Just a thought, but you could use a multiplexing pattern, where you spawn off a goroutine with 2 channels that blocks on both and sends the output to a new channel. Then you can just build up a tree of these dynamically from your list that funnels everything down to a single channel, which you then read on.
答案3
得分: 4
包主要
导入 "fmt"
功能 主要() {
c1 := make(chan int)
c2 := make(chan int)
去 功能() { c1 <- 1 }()
去 功能() { c2 <- 2 }()
cs := []chan int{c1, c2}
cm := make(chan [2]int)
对于 idx, c := 范围(cs) {
去 功能(idx int, c chan int) {
cm <- [2]int{idx, <-c}
}(idx, c)
}
fmt.Print(<-cm)
fmt.Print(<-cm)
}
打印 [0 1][1 2]
(或者可能是 [1 2][0 1]
)。
英文:
package main
import "fmt"
func main() {
c1 := make(chan int)
c2 := make(chan int)
go func() { c1 <- 1 }()
go func() { c2 <- 2 }()
cs := []chan int{c1, c2}
cm := make(chan [2]int)
for idx, c := range(cs) {
go func(idx int, c chan int) {
cm <- [2]int{idx, <-c}
}(idx, c)
}
fmt.Print(<-cm)
fmt.Print(<-cm)
}
prints [0 1][1 2]
(or maybe [1 2][0 1]
).
答案4
得分: 1
也许可以这样实现?
// multiplex函数接受一个chan int的切片,并返回一个chan int,用于在所有通道之间进行复用。
func multiplex(chs []<-chan int) <-chan int {
c := make(chan int)
d := make(chan bool)
for _, ch := range chs {
go func(ch <-chan int) {
for r := range ch {
c <- r
}
d <- true
}(ch)
}
go func() {
for i := 0; i < len(chs); i++ {
<-d
}
close(c)
}()
return c
}
英文:
Perhaps something like this may apply?
// multiplex takes a slice of chan ints and returns a channel
// that multiplexes between all of them.
func multiplex(chs []<-chan int) <-chan int {
c := make(chan int)
d := make(chan bool)
for _, ch := range chs {
go func(ch <-chan int) {
for r := range ch {
c <- r
}
d <- true
}(ch)
}
go func() {
for i := 0; i < len(chs); i++ {
<-d
}
close(c)
}()
return c
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论