Was ich tun möchte, ist eine Reihe von Produzenten Goroutines (von denen einige möglicherweise oder nicht abgeschlossen) und eine Verbraucher Routine. Das Problem ist mit diesem Vorbehalt in Klammern - wir kennen nicht die Gesamtzahl, die eine Antwort zurückgibt.Was ist das beste Idiom für Produzenten/Konsumenten in Go?
Also, was ich will, ist, dies zu tun:
package main
import (
"fmt"
"math/rand"
)
func producer(c chan int) {
// May or may not produce.
success := rand.Float32() > 0.5
if success {
c <- rand.Int()
}
}
func main() {
c := make(chan int, 10)
for i := 0; i < 10; i++ {
go producer(c, signal)
}
// If we include a close, then that's WRONG. Chan will be closed
// but a producer will try to write to it. Runtime error.
close(c)
// If we don't close, then that's WRONG. All goroutines will
// deadlock, since the range keyword will look for a close.
for num := range c {
fmt.Printf("Producer produced: %d\n", num)
}
fmt.Println("All done.")
}
So ist die Frage, ob ich es falsch ist zu schließen, wenn ich nicht in der Nähe zu tun - es ist immer noch falsch (Kommentare im Code sehen).
Nun wäre die Lösung ein Out-of-Band-Signalkanal sein, dass alle Produzenten zu schreiben:
package main
import (
"fmt"
"math/rand"
)
func producer(c chan int, signal chan bool) {
success := rand.Float32() > 0.5
if success {
c <- rand.Int()
}
signal <- true
}
func main() {
c := make(chan int, 10)
signal := make(chan bool, 10)
for i := 0; i < 10; i++ {
go producer(c, signal)
}
// This is basically a 'join'.
num_done := 0
for num_done < 10 {
<- signal
num_done++
}
close(c)
for num := range c {
fmt.Printf("Producer produced: %d\n", num)
}
fmt.Println("All done.")
}
Und das völlig tut, was ich will! Aber mir scheint es wie ein Bissen. Meine Frage ist: Gibt es einen Idiom/Trick, der es mir leichter macht, etwas Ähnliches zu tun?
hatte ich einen Blick hier: http://golang.org/doc/codewalk/sharemem/ Und es scheint, wie die complete
chan (zu Beginn der main
initialisiert) in einem Bereich verwendet wird, aber nie geschlossen. Ich verstehe nicht wie.
Wenn jemand irgendwelche Einsichten hat, würde ich es sehr schätzen. Prost!
Edit: fls0815 hat die Antwort und beantwortet auch die Frage, wie die enge losen Kanalbereich arbeitet.
Mein Code oben zu arbeiten modifed (done vor fls0815 freundlicherweise Code):
package main
import (
"fmt"
"math/rand"
"sync"
)
var wg_prod sync.WaitGroup
var wg_cons sync.WaitGroup
func producer(c chan int) {
success := rand.Float32() > 0.5
if success {
c <- rand.Int()
}
wg_prod.Done()
}
func main() {
c := make(chan int, 10)
wg_prod.Add(10)
for i := 0; i < 10; i++ {
go producer(c)
}
wg_cons.Add(1)
go func() {
for num := range c {
fmt.Printf("Producer produced: %d\n", num)
}
wg_cons.Done()
}()
wg_prod.Wait()
close(c)
wg_cons.Wait()
fmt.Println("All done.")
}
Hallo, danke für deine Antwort - es ist in der Tat, was ich suchte. Könnten Sie Ihren Vorschlag, "Nur Produzenten sollten Kanäle schließen", erweitern? - Es klingt wie eine Common-Sense/Code-macht-Sinn-Regel, aber ich frage mich, ob es auch einen technischen Grund gab (da das Codebeispiel, das du auflistet, die Hauptfunktion hat, den Kanal zu schließen). Danke noch einmal! – Will
Ich habe weitere Informationen hinzugefügt. HTH. – fls0815
Ahhh, das macht Sinn. Ich dachte, dass es vielleicht eine harte Regel sein könnte - in der jeder Produzent prüfen müsste, ob es erlaubt ist, den Kanal zu schließen (daher schließt der letzte, der ihn beendet). Das ist offensichtlich viel unordentlicher (mit mehr unnötigen Überprüfungen), als es einfach in main() in unseren Beispielen zu schließen, aber ich war besorgt, dass es die Art und Weise war, Dinge zu tun (aus irgendeinem Grund war mir das nicht bewusst). Ich versuche immer noch, ein Gefühl für Stil Best Practices zu bekommen. – Will