在Go语言中,动态创建通道以用作可变参数函数的参数。

huangapple go评论148阅读模式
英文:

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]&lt;-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]&lt;-chan int) as type []&lt;-chan int in 
argument to merge

Here is the full code...

package main
import (
&quot;fmt&quot;
&quot;sync&quot;
)
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&#39;d like to use instead of the above...
//var res [2]&lt;-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]&lt;-chan int) as type []&lt;-chan int in argument to merge
}
func gen() &lt;-chan int {
out := make(chan int)
go func() {
for i := 0; i &lt; 100; i++ {
for j := 3; j &lt; 13; j++ {
out &lt;- j
}
}
close(out)
}()
return out
}
func factorial(in &lt;-chan int) &lt;-chan int {
out := make(chan int)
go func() {
for n := range in {
out &lt;- fact(n)
}
close(out)
}()
return out
}
func fact(n int) int {
total := 1
for i := n; i &gt; 0; i-- {
total *= i
}
return total
}
func merge(cs ...&lt;-chan int) &lt;-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch &lt;-chan int) {
for n := range ch {
out &lt;- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}

答案1

得分: 0

你有两个问题:

  1. 可变参数函数接受多个参数(例如,fmt.Println 接受多个 interface{} 类型的参数)。而你正在发送一个数组。

  2. 大小为 [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:

  1. A variadic function takes multiple args (fmt.Println, for example, takes multiple interface{}). You're sending an array.

  2. 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 []&lt;-chan int) &lt;-chan int {
out := make(chan int)
var wg sync.WaitGroup
wg.Add(len(cs))
for _, c := range cs {
go func(ch &lt;-chan int) {
for n := range ch {
out &lt;- n
}
wg.Done()
}(c)
}
go func() {
wg.Wait()
close(out)
}()
return out

}

https://play.golang.org/p/zTrrsRkKJV

答案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 (
&quot;fmt&quot;
&quot;math/rand&quot;
&quot;reflect&quot;
&quot;sync&quot;
&quot;time&quot;
)
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 &lt; 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(&quot;I am chan-%v, value is %v\n&quot;, ch, v)
}
wg.Done()
}()
chans[rand.Int() % len(chans)] &lt;- true
wg.Wait()
}

huangapple
  • 本文由 发表于 2017年6月29日 06:36:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/44813598.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定