Search code examples
gogoroutine

Proper way to close a channel


I want to have a bunch of goroutines that will fetch some information from a lot of servers. I'm simplifying the code below so its more readable. It seems to be working perfectly but it panics after all the tasks were completed as I never close the channel. The thing is I am not sure where I should close the same.

I need your help in :

  • Telling me where in the code I should be closing the channel.
  • Telling me if the overall logic of this code seems idiomatically correct.

my code

func main() {
        ch := make(chan string)


        for i:= 0; i < 10 ; i++ {
                go func(c chan <- string,t int){
                        time.Sleep( time.Duration(rand.Intn(3000)) * time.Millisecond )
                        c <- strconv.Itoa(t) + " : Done " + strconv.Itoa(rand.Intn(3000))
                }(ch,i)
        }
        for val := range ch {
                fmt.Println(val)

        }
}

output

$ go run test_channels.go
0 : Done 1694
6 : Done 511
3 : Done 162
2 : Done 89
8 : Done 2728
5 : Done 1274
1 : Done 2211
9 : Done 1445
4 : Done 2237
7 : Done 1106
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/matias/projects/src/github.com/matias/test/test_channels.go:22 +0x138
exit status 2

Solution

  • When there's a single sender on a channel, that sender would normally be responsible for closing the channel if necessary. Remember though that closing a channel isn't required for cleanup, it's only if you need to signal that the channel is closed.

    When there are multiple senders, you need to coordinate with all of them completing, which you can do with a sync.waitGroup

    ch := make(chan string)
    var wg sync.WaitGroup
    
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(c chan<- string, t int) {
            defer wg.Done()
            time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
            c <- strconv.Itoa(t) + " : Done " + strconv.Itoa(rand.Intn(3000))
        }(ch, i)
    }
    
    go func() {
        wg.Wait()
        close(ch)
    }()
    

    https://play.golang.org/p/ViOtMHbi43C