Go by Example

Closing Channels

Close a channel to broadcast completion; only senders should close.

Closing a channel signals that no more values will be sent. All receivers immediately unblock and receive the zero value with an ok of false.

Use close(ch) after sending all values. A receiver can detect the close with the comma-ok idiom: v, ok := <-ch. When ok is false the channel is closed and drained.

package main
 
import "fmt"
 
func main() {
    jobs := make(chan int, 5)
 
    for i := 1; i <= 3; i++ {
        jobs <- i
    }
    close(jobs) // signal: no more jobs
 
    for {
        j, ok := <-jobs
        if !ok {
            fmt.Println("channel closed")
            break
        }
        fmt.Println("received job", j)
    }
}

A for range loop over a channel automatically stops when the channel is closed - the comma-ok check is implicit:

for j := range jobs {
    fmt.Println("job", j)
}
// loop exits when jobs is closed

Closing a channel that was already closed panics at runtime. Use a sync.Once when multiple goroutines might attempt to close the same channel:

var once sync.Once
 
closeJobs := func() {
    once.Do(func() { close(jobs) })
}

In production

Closing a channel is a broadcast signal - all receivers see the close immediately. Only the goroutine responsible for producing values should close the channel; closing from a receiver or a third party is a race condition. Use a sync.Once or a dedicated closer goroutine when multiple producers share one channel.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free