Learn Go← Dashboard

Go's typed pipes — communicate, don't share memory.

Buffered Channels, range, and close

Buffered channels

make(chan T, n) creates a channel that holds up to n values without a receiver. Sends only block when the buffer is full; receives only block when it's empty.

go playground
Loading...

Buffered channels are useful when:

  • a producer is bursty and you don't want it to block,
  • you want a fixed-size work queue between producers and workers,
  • you're implementing a rate limiter.

But: don't reach for a buffered channel just to "avoid deadlock". If you need a buffer to make your code not hang, you probably have a synchronization bug.

Closing a channel

close(ch) tells "no more values will ever be sent on this channel". Receivers can use the two-value form to detect closure:

v, ok := <-ch
if !ok {
    // channel was closed and drained
}

Or — much more commonly — they use for ... range:

go playground
Loading...

Channel rules of thumb

  • Only the sender should close a channel. Sending on a closed channel panics; receiving from a closed channel returns the zero value immediately.
  • If you have multiple senders, you'll need an extra signal (like another channel) to coordinate the close.
  • Receivers don't need to close — they just stop reading.
  • A nil channel blocks forever on both send and receive. Useful with select.

| Operation | Result | | ------------------------- | ----------------------------------------------- | | Send on a closed channel | panic | | Send on a nil channel | blocks forever | | Receive on a closed channel | zero value immediately, ok==false | | Receive on a nil channel | blocks forever | | Close a closed channel | panic | | Close a nil channel | panic |

A common pattern: worker pool

go playground
Loading...
What happens if you `close(ch)` and then try to send on `ch`?