2010-11-18 7 views
8

Go hat einen Mechanismus, um einen blockierenden Lesevorgang von einem von mehreren Kanälen auszuführen, die select Anweisung. So können Sie sagenWie wählt man eine Eingabe für eine dynamische Liste von Kanälen in Go?

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

wird blockiert, bis wir Eingang von einem dieser beiden Kanäle erhalten. Sehr schön.

Aber das erfordert, dass ich im Quellcode angeben, wie viele Kanäle ich abfragen möchte. Was ist, wenn ich eine Scheibe oder ein Array von Kanälen habe und blockieren möchte, bis ich eine Eingabe von ihnen bekomme?

Antwort

4

Nur ein Gedanke, aber Sie könnten ein Multiplexing-Muster verwenden, wo Sie eine Goroutine mit 2 Kanälen spawnen, die auf beiden blockiert und die Ausgabe an einen neuen Kanal sendet. Dann können Sie einfach einen Baum dynamisch von Ihrer Liste aufbauen, der alles auf einen Kanal herunterleitet, den Sie dann weiterlesen.

+0

Das ist, was ich dachte auch, ich nur gefragt, ob es ein direkter Weg war. – poolie

+0

Könnten Sie sich fragen, wie Sie dies bei der Scheibe erreichen würden? –

+0

@Matt finden Sie unter [Ephemient's Antwort] (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) 
} 

druckt [0 1][1 2] (oder vielleicht [1 2][0 1]).

+0

Also das ist im Grunde genommen, was Kevin Ballard gesagt hat, dass Sie neue goroutines machen können, die von wie vielen Kanälen Sie in einer statisch definierten Anzahl von Zielen saugen. Meinetwegen. – poolie

+0

Also für jeden Kanal, erstellen Sie eine andere Goroutine, die von diesem Kanal zieht und es auf den Multiplexkanal setzt? –

+0

@MattJoiner, richtig. Ich denke, es gibt keinen Grund, dass es 1: 1 sein muss, aber da Göroutinen billig sind, sehe ich keinen * a priori * Grund, es nicht so zu machen. – poolie

0

Vielleicht kann so etwas zutreffen?

// 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

Seit go1.1 gibt es eine richtige API, um ausgewählte Sätze dynamisch auszuführen.

Hier ist ein komplettes und benutzbar Beispiel:

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

Sie damit interaktiv spielen, um können the playground