I need to process several tasks concurrently and then "collect" the results. Below is the code I came up with but I'm wondering if it's the right way to do this(i.e idiomatic/ best practice) or if there is something wrong that I might miss.
package main
import "fmt"
import "sync"
func main() {
// ch is the int provider. Cap is 99 but it should
// really be 3
ch := make(chan int, 99)
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
for v := range ch {
fmt.Println("consume ", v)
if len(ch) == 0 {
close(ch)
}
}
fmt.Printf("All done")
}
There's nothing wrong with that .. it works. However, it really should be the producers job to close the channel (not the consumer).
To that end .. I would propose that you move the entire producer process into a goroutine and have that wait .. then close the channel:
package main
import "fmt"
import "sync"
func main() {
ch := make(chan int, 3)
var wg sync.WaitGroup
go func() {
for i := 0; i < 3; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
ch <- i
}(i)
}
wg.Wait()
close(ch) // producer is now closing the channel
}()
// Consumer has the single responsibility of iterating over the channel
for v := range ch {
fmt.Println("consume ", v)
}
fmt.Printf("All done")
}
I am sure your example is contrived, but your example could be made much simpler:
ch := make(chan int, 99)
for i := 0; i < 3; i++ {
go func(i int) {
ch <- i
}(i)
}
for i := 0; i < 3; i++ {
v := <- ch
fmt.Println("consume ", v)
}
fmt.Println("All done")
Your real code may be more complex and require something like a waitGroup, and if that is the case please do whatever you can to explain your specific problem.
Closing a channel within a range in order to exit the range seems like non-idiomatic Go.