如何退出频道范围/收集结果

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

See it in the Go Playground

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.

Go Play