What happens to goroutines that are already blocked on a channel when it is closed?
Question 218HardGo 1.22 to 1.25
closechan sets the closed flag under the channel lock, removes every waiting sudog from both recvq and sendq, and makes all of those goroutines runnable:
- Blocked receivers wake up with the zero value and
ok == false. On an unbuffered channel, or a buffered one that is empty, nothing is left to drain. - Blocked senders wake up and panic with
send on closed channel, even though their send started before the close.
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
for i := range 3 {
go func() {
defer func() { fmt.Println(i, recover()) }()
ch <- i // all three park here
}()
}
time.Sleep(100 * time.Millisecond) // demo only: let them block
close(ch)
time.Sleep(100 * time.Millisecond)
}
This prints three lines like 1 send on closed channel, in any order. Without the recover, the first panic would crash the whole program.
Why interviewers ask: it shows why "the receiver closes to tell senders to stop" is wrong. Senders that are in flight at that moment crash. A close is only safe once no send can still happen, which is why the owner/single-closer rule exists and why stop signals go through a separate done channel or context.
More on Channels & select
- Q216How do you gracefully shut down a service that uses channels and goroutines?
- Q217What does this print? (
breakinsideselectinsidefor) - Q219Go has no unbounded channels. Why not, and how would you build one?
- Q220What exactly gets copied when you send on a channel, and what limits apply to element types and buffer sizes?