A channel can only be closed once, attempting to close a closed channel panics.
But receiving from a closed channel is not limited, receiving from a closed channel:
A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
A Go app runs until its main goroutine runs (given "normal" circumstances), or from another point of view: a Go app terminates when its main goroutine terminates, that is, the main()
function returns. It does not wait for other non-main
goroutines to complete.
You started a 2nd goroutine with an endless for
loop, with no way of terminating. So that loop will keep going until the main()
function –which runs in the concurrent, main goroutine– returns. Since the for
loop first receives from jobs
, it waits for the main goroutine to close it (this receive operation can only then proceed). Then the main goroutine wants to receive from done
, so that waits until the 2nd goroutine sends a value on it. Then the main goroutine is "free" to terminate at any moment. Your 2nd goroutine running the loop may receive an additional value from jobs
since it's closed, but the subsequent send on done
will block as there are nobody receiving from it anymore (and it's unbuffered).
Receiving from a channel until it's closed is normally done using for range
, which exits if the channel is closed:
for j := range jobs {
fmt.Println("received: ", j)
done <- true
}
Of course this would cause a deadlock in your case, as the loop body would never be reached as nobody sends anything on jobs
, and so the loop would never enter its body to send a value on done
which is what the main goroutine waits for.