英文:
Dynamically creating channels to use as arguments to variadic function in Go
问题
在Go语言中,动态创建一个通道并为其分配一个值是可能的。你可以使用以下代码实现fan-out-fan-in模式:
f1 := factorial(in)
f2 := factorial(in)
for n := range merge(f1, f2) {
fmt.Println(n)
}
但是,如果你想要实现以下类似的操作:
var res [2]<-chan int
res[0] = make(chan int)
res[0] = factorial(in)
res[1] = make(chan int)
res[1] = factorial(in)
for n := range merge(res...) {
fmt.Println(n)
}
这将导致以下错误:
$ go run main.go
# command-line-arguments
.\main.go:26: cannot use res (type [2]<-chan int) as type []<-chan int in
argument to merge
问题出在merge
函数的参数类型不匹配。merge
函数期望接收一个[]<-chan int
类型的参数,而不是[2]<-chan int
类型。要解决这个问题,你可以将res
声明为切片类型[]<-chan int
,然后使用append
函数将通道添加到切片中。修改后的代码如下:
package main
import (
"fmt"
"sync"
)
func main() {
in := gen()
f1 := factorial(in) // code to be replaced
f2 := factorial(in) // code to be replaced
for n := range merge(f1, f2) { // code to be replaced
fmt.Println(n) // code to be replaced
} // code to be replaced
// What I'd like to use instead of the above...
var res []<-chan int
res = append(res, make(chan int))
res[0] = factorial(in)
res = append(res, make(chan int))
res[1] = factorial(in)
for n := range merge(res...) {
fmt.Println(n)
}
}
func gen() <-chan int {
out := make(chan int)
go func() {
for i := 0; i < 100; i++ {
for j := 3; j < 13; j++ {
out <- j
}
}
close(out)
}()
return out
}
func factorial(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- fact(n)
}
close(out)
}()
return out
}
func fact(n int) int {
total := 1
for i := n; i > 0; i-- {
total *= i
}
return total
}
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch <-chan int) {
for n := range ch {
out <- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
现在,你可以使用切片res
来动态创建通道,并将它们分配给factorial
函数。然后,你可以将res...
作为参数传递给merge
函数,实现fan-out-fan-in模式。
英文:
Is it possible to dynamically create a channel in Go and then assign it a value? This fan-out-fan-in code works with
f1 := factorial(in)
f2 := factorial(in)
for n := range merge(f1, f2) {
fmt.Println(n)
}
But I'd like to do something like
var res [2]<-chan int
res[0] = make(chan int)
res[0] = factorial(in)
res[1] = make(chan int)
res[1] = factorial(in)
for n := range merge(res...)
fmt.Println(n)
}
This gives the following error
$ go run main.go
# command-line-arguments
.\main.go:26: cannot use res (type [2]<-chan int) as type []<-chan int in
argument to merge
Here is the full code...
package main
import (
"fmt"
"sync"
)
func main() {
in := gen()
f1 := factorial(in) // code to be replaced
f2 := factorial(in) // code to be replaced
for n := range merge(f1, f2) { // code to be replaced
fmt.Println(n) // code to be replaced
} // code to be replaced
// What I'd like to use instead of the above...
//var res [2]<-chan int
//res[0] = make(chan int)
//res[0] = factorial(in)
//res[1] = make(chan int)
//res[1] = factorial(in)
// for n := range merge(res...)
// fmt.Println(n)
//}
// This commented code generates the following error
//$ go run main.go
//# command-line-arguments
//.\main.go:20: cannot use res (type [2]<-chan int) as type []<-chan int in argument to merge
}
func gen() <-chan int {
out := make(chan int)
go func() {
for i := 0; i < 100; i++ {
for j := 3; j < 13; j++ {
out <- j
}
}
close(out)
}()
return out
}
func factorial(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- fact(n)
}
close(out)
}()
return out
}
func fact(n int) int {
total := 1
for i := n; i > 0; i-- {
total *= i
}
return total
}
func merge(cs ...<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch <-chan int) {
for n := range ch {
out <- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
答案1
得分: 0
你有两个问题:
-
可变参数函数接受多个参数(例如,fmt.Println 接受多个 interface{} 类型的参数)。而你正在发送一个数组。
-
大小为 [2] 的数组与大小为 [] 的数组不同(是的,在 Go 中,数组的大小是类型的一部分)。
你想要的是类似这样的代码:
for n := range merge(res[:]) {
...
func merge(cs []<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch <-chan int) {
for n := range ch {
out <- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
https://play.golang.org/p/zTrrsRkKJV
英文:
You have two problems:
-
A variadic function takes multiple args (fmt.Println, for example, takes multiple interface{}). You're sending an array.
-
An array which is size [2] is not the same as size [] (yes, size of array is part of the type in go).
What you want is something like:
for n := range merge(res[:]) {
...
func merge(cs []<-chan int) <-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch <-chan int) {
for n := range ch {
out <- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
答案2
得分: 0
选择每个goroutine的通道会消耗资源。当你有第N个通道时,goroutine应该是第N个。你应该使用reflect.Select
。
package main
import (
"fmt"
"math/rand"
"reflect"
"sync"
"time"
)
func multpleSelect(chans []chan bool) (int, bool, bool) {
cases := make([]reflect.SelectCase, len(chans))
for i, ch := range chans {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
}
}
i, v, ok := reflect.Select(cases)
return i, v.Interface().(bool), ok
}
func main() {
rand.Seed(time.Now().UnixNano())
chans := make([]chan bool, 100)
for i := 0; i < 100; i++ {
chans[i] = make(chan bool)
}
var wg sync.WaitGroup
go func() {
wg.Add(1)
if ch, v, ok := multpleSelect(chans); ok {
fmt.Printf("我是通道-%v,值为%v\n", ch, v)
}
wg.Done()
}()
chans[rand.Int() % len(chans)] <- true
wg.Wait()
}
希望这对你有帮助!
英文:
Selecting chan per goroutine take resources. When you have N'th channel, goroutine should be N'th. You should use reflect.Select
package main
import (
"fmt"
"math/rand"
"reflect"
"sync"
"time"
)
func multpleSelect(chans []chan bool) (int, bool, bool) {
cases := make([]reflect.SelectCase, len(chans))
for i, ch := range chans {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(ch),
}
}
i, v, ok := reflect.Select(cases)
return i, v.Interface().(bool), ok
}
func main() {
rand.Seed(time.Now().UnixNano())
chans := make([]chan bool, 100)
for i := 0; i < 100; i++ {
chans[i] = make(chan bool)
}
var wg sync.WaitGroup
go func() {
wg.Add(1)
if ch, v, ok := multpleSelect(chans); ok {
fmt.Printf("I am chan-%v, value is %v\n", ch, v)
}
wg.Done()
}()
chans[rand.Int() % len(chans)] <- true
wg.Wait()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论