2015-07-29 3 views
3

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

+1

Nope. Вы можете сделать неблокирующий прием (используя «select» с одним случаем получения и «по умолчанию») на канале, чтобы не блокировать, если 'b' не готов (но тогда вы уже взяли значение из' a '), и вы можете сделать' len() 'на буферизованном канале, чтобы спекулятивно проверить, сколько элементов находится в буфере (хотя это может измениться до того, как ваш прием будет выполнен). – twotwotwo

ответ

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

Я предполагаю, что мой пример не был особенно ясен. Если бы я просто читал (a, b) и (c, d), тогда простое решение состоит в том, чтобы иметь два отдельных goroutines. Но предположим, что есть три канала (a, b, c), и одна ветка считывает из (a, b), а другая ветвь читает из (a, c)? –

+2

@BrianCandler: Я пропустил комментарий о сетях Петри. Это не то, что вы можете делать только с каналами, и я чувствую, что CSP не будет полезен для представления этого (хотя возможно и обратное). Чтобы определить, может ли несколько каналов быть успешным, а затем работать с ними, вам нужно остановить все остальные параллельные процессы, сделав то же самое. Горутины и каналы, похоже, не подходят для проблемного пространства. – JimB

+0

Спасибо @JimB, вот ответ, который я искал. –

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) 
} 
Смежные вопросы