Проверка одновременных каналов, готовых к работе

Я хотел бы знать, позволяет ли язык go проверять одновременность готовности нескольких каналов.

Вот несколько надуманный пример того, что я пытаюсь сделать. (Фактическая причина состоит в том, чтобы посмотреть, смогу ли я внедрить петринец в ходу)

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: только когда все значения готовы.

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

Отдельные каналы для двух разных пар, которые вы накапливаете, могут работать:

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)
}
Другие вопросы по тегам