【问题标题】:Checking for simultaneous channels ready in go检查同步通道是否准备就绪
【发布时间】:2015-10-20 19:55:14
【问题描述】:

我想知道 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 上的值准备好时,我不能提交到任一分支:只有当所有值都准备好时。

【问题讨论】:

  • 不。如果b 尚未准备好,您可以在通道上进行非阻塞接收(使用select 和一个接收案例和default)以不阻塞(但是您已经从@987654327 中获取了一个值@),并且您可以在缓冲通道上执行 len() 来推测性地检查缓冲区中有多少项目(尽管在您的接收执行之前这可能会改变)。

标签: go channel petri-net


【解决方案1】:

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

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

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]
        }
    }
}

【讨论】:

  • 我想我的例子不是特别清楚。如果我只是从 (a,b) 和 (c,d) 中读取,那么简单的解决方案是拥有两个单独的 goroutine。但是假设有 3 个通道 (a,b,c),一个分支从 (a,b) 读取,另一个分支从 (a,c) 读取?
  • @BrianCandler:我跳过了关于 Petri 网的评论。这不是你可以单独使用频道做的事情,而且我觉得 CSP 在表示这一点时不会有用(尽管反过来也是可能的)。为了确定多个通道是否可以成功并对其进行操作,您需要停止所有其他并发进程执行相同操作。 Goroutines 和通道似乎不太适合问题空间。
  • 谢谢@JimB,这就是我一直在寻找的答案。
【解决方案2】:

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

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)
}

【讨论】:

    猜你喜欢
    • 2016-04-07
    • 2011-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    相关资源
    最近更新 更多