Closing Channels and range
close marks a channel finished so a receiver can tell "no value yet" apart from "no more values ever".
close(ch) // then: v, ok := <-ch or for v := range chA receiver often needs to know that nothing more is coming. close(ch) records exactly that. It does not destroy the channel and it does not discard values already sent.
After a close, receives keep working. Values still sitting in the channel come out as normal. Once it is empty, every further receive returns immediately with the element type's zero value: 0 for a number, "" for a string, nil for a pointer.
Telling a real value from a closed channel
The two-result form of a receive reports which happened. v, ok := <-ch sets ok to true when a send delivered v, and false when v is a zero value produced because the channel is closed and empty.
for v := range ch is the shorthand for that check. It receives values until the channel is closed and drained, then the loop ends by itself. It is the same as a bare for loop doing a two-result receive and breaking when ok is false.
Who closes, and what panics
The sender closes. A receiver cannot know whether more sends are coming, and the language will not let it try: close on a receive-only channel does not compile.
Three things panic at runtime. Sending on a closed channel panics with send on closed channel. Closing an already closed channel panics with close of closed channel. Closing a nil channel panics with close of nil channel.
With several senders, none of them may close. Have one goroutine wait for all of them to finish, then close.
Example
package main
import "fmt"
// The sender closes, because only the sender knows nothing more is coming.
func send(lines chan<- string) {
lines <- "first"
lines <- "second"
lines <- "third"
close(lines)
}
func main() {
lines := make(chan string)
go send(lines)
// range over a channel keeps receiving until the channel is closed
// and drained, then the loop ends on its own.
for line := range lines {
fmt.Println("line:", line)
}
// A receive from a closed, empty channel returns at once with the
// zero value. The second result reports whether a send delivered it.
line, ok := <-lines
fmt.Printf("after close: %q ok=%v\n", line, ok)
}
// Output:
// line: first
// line: second
// line: third
// after close: "" ok=falseWhen to use it
- End a consumer's range loop once the producer has sent its last database row.
- Close a quit channel to cancel every worker goroutine at once.
- Tell a caller a stream ended normally rather than returning a zero value it cannot interpret.
- Shut down a fan-in collector after the last worker has reported.
More examples
range over a channel, and the same loop written out
The shorthand and the longhand of the same loop, with the same answer. <code>for v := range ch</code> is a two-result receive plus a break on <code>!ok</code>, nothing more. Knowing the long form matters for two reasons. You will meet it in code that needs to do something extra when the channel closes, rather than just leaving the loop. And it is the form you must use inside a <code>select</code>, where <code>range</code> is not available, so a case written <code>case v, ok := <-ch:</code> is doing exactly this check by hand.
package main
import "fmt"
func fill() chan int {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
return ch
}
func main() {
// Shorthand. The loop ends by itself once the channel is closed
// and everything already sent has been received.
sum := 0
for v := range fill() {
sum += v
}
fmt.Println("range sum:", sum)
// Longhand. This is what the range loop above compiles down to.
ch := fill()
sum = 0
for {
v, ok := <-ch
if !ok {
break
}
sum += v
}
fmt.Println("manual sum:", sum)
}
// Output:
// range sum: 6
// manual sum: 6Closing as a broadcast to many goroutines
The reason <code>close</code> is more than an end-of-data marker. A value sent on a channel goes to exactly one receiver, so a send cannot tell three goroutines to stop. A close is visible to every receiver, immediately and permanently, so one <code>close(quit)</code> releases all of them however many there are. This is the standard shutdown signal in long-running services, and the channel type is <code>chan struct{}</code> because no data travels, only the fact of the close. <code>struct{}</code> is a type with no fields, so its values take no space.
package main
import (
"fmt"
"sync"
)
func main() {
// quit never carries a value. Closing it is the signal. Every receiver
// sees a close, which is something a single send could never do.
quit := make(chan struct{})
released := make(chan int, 3)
var wg sync.WaitGroup
for id := 1; id <= 3; id++ {
wg.Go(func() {
<-quit // waits here until quit is closed
released <- id
})
}
close(quit) // frees all three goroutines at once
wg.Wait()
fmt.Println("goroutines released:", len(released))
}
// Output:
// goroutines released: 3Several senders, one owner of the close
The rule "the sender closes" needs amending once there is more than one sender: the last sender closes, and no individual sender knows that it is the last. The answer is a <code>sync.WaitGroup</code> and a goroutine whose only job is to wait and then close. Get this wrong and you have a crash that only appears under load, because it needs two goroutines to reach the same instant. The panic text is <code>panic: send on closed channel</code>, and the stack trace points at the send, not at the close that caused it.
package main
import (
"fmt"
"sync"
)
func main() {
out := make(chan int, 4)
// Two senders. Neither of them may close out: the first close would
// make the other's next send panic with "send on closed channel".
var wg sync.WaitGroup
wg.Go(func() { out <- 1 })
wg.Go(func() { out <- 2 })
// A separate goroutine owns the close. It runs only after every sender
// has finished, so no send can ever happen after the close.
go func() {
wg.Wait()
close(out)
}()
total := 0
for v := range out {
total += v
}
fmt.Println("total:", total)
}
// Output:
// total: 3
Discussion