4

我想知道 go 语言是否允许同时检查多个通道是否准备就绪。

这是我正在尝试做的一个有点做作的例子。(实际原因是看能不能在go中原生实现petrinet)

package main

import "fmt"

func mynet(a, b, c, d <-chan int, res chan<- int) {
    for {
        select {
        case v1, v2 := <-a, <-b:
            res <- v1+v2
        case v1, v2 := <-c, <-d:
            res <- v1-v2
        }
    }
}

func main() {
        a := make(chan int)
        b := make(chan int)
        c := make(chan int)
        d := make(chan int)
        res := make(chan int, 10)
        go mynet(a, b, c, d, res)

        a <- 5
        c <- 5
        d <- 7
        b <- 7
        fmt.Println(<-res)
        fmt.Println(<-res)
}

如图所示,这不会编译。它可以通过仅检查一个通道来编译,但是如果该通道准备好而另一个通道没有准备好,它可能会很容易死锁。

package main

import "fmt"

func mynet(a, b, c, d <-chan int, res chan<- int) {
    for {
        select {
        case v1 := <-a:
            v2 := <-b
            res <- v1+v2
        case v1 := <-c:
            v2 := <-d
            res <- v1-v2
        }
    }
}

func main() {
        a := make(chan int)
        b := make(chan int)
        c := make(chan int)
        d := make(chan int)
        res := make(chan int, 10)
        go mynet(a, b, c, d, res)

        a <- 5
        c <- 5
        d <- 7
        //a <- 5
        b <- 7
        fmt.Println(<-res)
        fmt.Println(<-res)
}

在一般情况下,我可能有多个案例在同一个频道上等待,例如

case v1, v2 := <-a, <-b:
...
case v1, v2 := <-a, <-c:
...

所以当一个值在通道 a 上准备好时,我不能提交到任何一个分支:只有当所有值都准备好时。

4

2 回答 2

2

您不能同时在多个频道上进行选择。您可以做的是实现一个扇入模式,用于从多个渠道合并您的值。

基于您的代码的粗略示例可能如下所示:

func collect(ret chan []int, chans ...<-chan int) {
    ints := make([]int, len(chans))
    for i, c := range chans {
        ints[i] = <-c
    }
    ret <- ints
}

func mynet(a, b, c, d <-chan int, res chan<- int) {
    set1 := make(chan []int)
    set2 := make(chan []int)
    go collect(set1, a, b)
    go collect(set2, c, d)
    for {
        select {
        case vs := <-set1:
            res <- vs[0] + vs[1]
        case vs := <-set2:
            res <- vs[0] + vs[1]
        }
    }
}
于 2015-07-29T17:49:49.980 回答
0

您正在累积的两个不同对的单独通道可能会起作用:

package main

import "fmt"

func mynetPlus(a, b <-chan int, res chan<- int) {
    for {
        select {
        case v1 := <-a:
            v2 := <-b
            res <- v1 + v2
        case v2 := <-b:
            v1 := <-a
            res <- v1 + v2
        }
    }
}

func mynetMinus(c, d <-chan int, res chan<- int) {
    for {
        select {
        case v1 := <-c:
            v2 := <-d
            res <- v1 + v2
        case v2 := <-d:
            v1 := <-c
            res <- v1 + v2
        }
    }
}

func main() {
    a := make(chan int)
    b := make(chan int)
    c := make(chan int)
    d := make(chan int)
    res := make(chan int, 10)
    go mynetPlus(a, b, res)
    go mynetMinus(c, d, res)

    a <- 5
    c <- 5
    d <- 7
    //a <- 5
    b <- 7
    fmt.Println(<-res)
    fmt.Println(<-res)
}
于 2015-07-29T17:59:42.463 回答