Co chciałbym zrobić, to mieć zestaw producentów goroutin (z których niektórzy mogą lub nie muszą się zakończyć) i rutyny konsumenckiej. Problem polega na tym zastrzeżeniu w nawiasach - nie znamy całkowitej liczby, która zwróci odpowiedź.Jaki jest najładniejszy idiom dla producenta/konsumenta w Go?
Więc to, co chcę zrobić to w ten sposób:
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.")
}
Więc problem jest, jeśli zamknę to się stało, gdybym nie blisko - to jeszcze nie tak (patrz komentarze w kodzie).
Teraz rozwiązaniem byłoby out-of-band kanałowy sygnał, że wszyscy producenci pisać na adres:
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.")
}
I to zupełnie nie to, co chcę! Ale dla mnie wydaje się, że to kęs. Moje pytanie brzmi: czy istnieje jakiś idiom/sztuczka, która pozwala mi zrobić coś podobnego w łatwiejszy sposób?
miałem okiem tutaj: http://golang.org/doc/codewalk/sharemem/ I wydaje się, że complete
chan (zainicjowanego na początku main
) stosuje się w zakresie, ale nigdy nie została zamknięta. Nie rozumiem jak.
Jeśli ktoś ma jakiekolwiek spostrzeżenia, byłbym bardzo wdzięczny. Twoje zdrowie!
Edit: fls0815 ma odpowiedzi, a także odpowiedzi na pytanie, w jaki sposób działa zasięg kanał zbliżenie mniej.
Mój kod powyżej zmodyfikowaliśmy do pracy (zrobione przed fls0815 kodu uprzejmie dostarczonego):
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.")
}
Cześć, dzięki za odpowiedź - to rzeczywiście to, czego szukasz. Czy możesz rozszerzyć swoją sugestię, że "Tylko producenci powinni zamknąć kanały". - Brzmi to jak zasada zdrowego rozsądku/zasady kodowania, ale zastanawiałem się, czy był też jakiś techniczny powód (ponieważ przykładowy kod, którego lista zawiera główną funkcję zamykającą kanał). Dzięki jeszcze raz! – Will
Dodałem trochę więcej informacji. HTH. – fls0815
Ahhh, to ma sens. Pomyślałem, że może to być trudna zasada - w której każdy producent musiałby sprawdzić, czy można zamknąć kanał (dlatego ostatni, który kończy, zamyka go). To oczywiście dużo bardziej nieprzyjemne (z bardziej niepotrzebnymi kontrolami) niż zamykanie go w main() w naszych przykładach, ale martwiłem się, że to jest sposób na robienie rzeczy (z jakiegoś powodu byłem nieświadomy). Nadal staram się poznać styl najlepszych praktyk. – Will