缓冲通道并关闭

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

  1. When I place the close at Pos1, it works fine. But I am not sure why it works. The buffer cannot hold more than 2 elements at any given time, so when 2 elements are written, the loop will block until the main routing does a read. 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. Why does close work at this position?
  2. When I put it as position 2, it prints only 2 elements which kind of makes sense but why is the for loop not throwing an exception when it is trying to write more elements to the channel that is closed?
  3. When I close at Pos3, I get an exception 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.