I have a snippet of code that I am trying to understand based on how I put the close
call and the location
func main() {
ch := make(chan int, 2)
go func(ch chan int) {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Println("Func goroutine sends data: ", i)
}
//Pos1 - Works perfectly
//close(ch)
}(ch)
fmt.Println("Main goroutine sleeps 2 seconds")
time.Sleep(time.Second * 2)
fmt.Println("Main goroutine begins receiving data")
//Pos2 - Puts in only 2 ints 1 and 2 and then prints only that
//close(ch)
for d := range ch {
fmt.Println("Main goroutine received data:", d)
}
//Pos3 - Throws fatal error
close(ch)
}
I have been trying to understand and read blogs on this but not able to understand somethings still
close
work at this position?for loop
not throwing an exception when it is trying to write more elements to the channel that is closed?fatal error: all goroutines are asleep - deadlock!
though all 5 ints are printed. Why is that?But I thought to do a range over a buffered channel, the range function has to know beforehand how many elements to iterate over and for that channel must be closed.
That assumption is wrong and the root of all misunderstandings.
The behavior of ranging over a channel is described in the Go Spec: https://golang.org/ref/spec#For_statements
For channels, the iteration values produced are the successive values sent on the channel until the channel is closed. If the channel is nil, the range expression blocks forever.
The channel does not need to be closed when the for statement is evaluated and the statement does not need to know number of elements.
So, in your code, when you put close
in Pos1, it is indeed the right way to do it. When you put it in Pos3, the for loop waits the channel to be closed, which can only happen after the for loop itself, so it is a deadlock.
Putting close
in Pos2 is buggy and the behavior is a little tricky. It is possible to raise an error, yet it is possible to just output two numbers. This is because when the channel is closed before the for loop, the loop can run without block and then main()
returns. And when main()
returns, the Go program ends. Whether it raises an error solely depends on if the scheduler switch to goroutine between the process, which is not guaranteed.