Что бы я хотел сделать, так это иметь набор продюсеров goroutines (из которых некоторые могут или не могут быть завершены) и пользовательскую процедуру. Проблема заключается в этом предостережении в круглых скобках - мы не знаем общее число, которое вернет ответ.
Итак, что я хочу сделать, это:
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 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 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.")
}
Итак, проблема в том, что если я закрываю ее неправильно, если я не закрываю ее - она по-прежнему не так (см. комментарии в коде).
Теперь решение будет внеполосным сигнальным каналом, который ВСЕ производители пишут:
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.")
}
И это полностью делает то, что я хочу! Но для меня это похоже на глоток. Мой вопрос: есть ли какая-либо идиома/трюк, которая позволяет мне сделать что-то подобное более простым способом?
Я посмотрел здесь: http://golang.org/doc/codewalk/sharemem/
И кажется, что complete
chan (инициализированный в начале main
) используется в диапазоне, но никогда не закрывается. Я не понимаю, как.
Если у кого-нибудь есть идеи, я был бы очень признателен. Ура!
Изменить: у fls0815 есть ответ, а также ответили на вопрос о том, как работает диапазон каналов с более низким уровнем.
Мой код выше, модифицированный для работы (выполняется до любезного кода fls0815):
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.")
}