2010-11-18 5 views
8

Goには、いくつかのチャネルのうちの1つ(selectステートメント)からのブロッキング読み出しを行うメカニズムがあります。したがって、これらの2つのチャンネルのいずれかから入力が得られるまで、Goの動的なチャンネルリストの入力方法を選択するにはどうすればよいですか?

select { 
    case <- c1: 
    case <- c2: 
} 

がブロックされます。非常に素晴らしい。

しかし、これは、ソースコードでポーリングするチャンネルの数を指定する必要があります。スライスやチャンネルの配列を持っていて、そのいずれかに入力が入るまでブロックしたいのですが?

答えて

4

だけ考えていますが、両方で2つのチャンネルのブロックとゴルーチンをオフに産卵し、新しいチャネルに出力を送信多重化パターンを、使用することができます。次に、リストからダイナミックにツリーを構築して、すべてを1つのチャンネルに集めて読み込むことができます。

+0

に対話的にそれで遊ぶことができます。

はここで完了し、使用可能な例です。 – poolie

+0

スライスを考えれば、これをどのように達成するのでしょうか? –

+0

@Matt [ephemientの回答](http://stackoverflow.com/questions/4220745/how-to-select-for-input-on-a-dynamic-list-of-channels-in-go/4221081#4221081) )。 – poolie

4
package main 

import "fmt" 

func main() { 
    c1 := make(chan int) 
    c2 := make(chan int) 

    go func() { c1 <- 1 }() 
    go func() { c2 <- 2 }() 

    cs := []chan int{c1, c2} 
    cm := make(chan [2]int) 

    for idx, c := range(cs) { 
     go func(idx int, c chan int) { 
      cm <- [2]int{idx, <-c} 
     }(idx, c) 
    } 

    fmt.Print(<-cm) 
    fmt.Print(<-cm) 
} 

プリント[0 1][1 2](または多分[1 2][0 1])。

+0

これは、基本的には、ケビン・バラードが言ったことを綴っています。静的に定義された数の宛先から多くのチャンネルを吸い取って新しいゴルーチンを作ることができます。けっこうだ。 – poolie

+0

したがって、各チャンネルについて、そのチャンネルからプルして多重チャンネルに入れる別のゴルーチンを作成しますか? –

+0

@MattJoiner、正しい。私は1:1にする必要がある理由はないと思うが、ゴルーチンが安いので先験的な*理由がないように見える。 – poolie

0

おそらくこのようなものが適用される場合がありますか?

// multiplex takes a slice of chan ints and returns a channel 
// that multiplexes between all of them. 
func multiplex(chs []<-chan int) <-chan int { 
    c := make(chan int) 
    d := make(chan bool) 
    for _, ch := range chs { 
     go func(ch <-chan int) { 
      for r := range ch { 
       c <- r 
      } 
      d <- true 
     }(ch) 
    } 
    go func() { 
     for i := 0; i < len(chs); i++ { 
      <-d 
     } 
     close(c) 
    }() 
    return c 
} 
5

go1.1以降、選択セットを動的に行うための適切なAPIがあります。

package main 

import (
    "log" 
    "reflect" 
) 

func sendToAny(ob int, chs []chan int) int { 
    set := []reflect.SelectCase{} 
    for _, ch := range chs { 
     set = append(set, reflect.SelectCase{ 
      Dir: reflect.SelectSend, 
      Chan: reflect.ValueOf(ch), 
      Send: reflect.ValueOf(ob), 
     }) 
    } 
    to, _, _ := reflect.Select(set) 
    return to 
} 

func recvFromAny(chs []chan int) (val int, from int) { 
    set := []reflect.SelectCase{} 
    for _, ch := range chs { 
     set = append(set, reflect.SelectCase{ 
      Dir: reflect.SelectRecv, 
      Chan: reflect.ValueOf(ch), 
     }) 
    } 
    from, valValue, _ := reflect.Select(set) 
    val = valValue.Interface().(int) 
    return 
} 

func main() { 
    channels := []chan int{} 
    for i := 0; i < 5; i++ { 
     channels = append(channels, make(chan int)) 
    } 

    go func() { 
     for i := 0; i < 10; i++ { 
      x := sendToAny(i, channels) 
      log.Printf("Sent %v to ch%v", i, x) 
     } 
    }() 

    for i := 0; i < 10; i++ { 
     v, x := recvFromAny(channels) 
     log.Printf("Received %v from ch%v", v, x) 
    } 
} 

あなたは、私はあまりにも、私は直接的な方法があったのか疑問に思っ考えたものだthe playground

関連する問題