Channels are Go's primary way for goroutines to communicate and synchronise.
A channel is a typed conduit between goroutines: ch := make(chan int). ch <- v sends, v := <-ch receives. Go's motto: share memory by communicating.
An unbuffered channel synchronises: a send blocks until a receiver takes the value, so it doubles as a handshake. A buffered channel, make(chan int, 10), lets sends proceed until the buffer is full and receives proceed while it has values. Buffers smooth out bursts; they don't fix a design where nobody receives.
Closing: close(ch) tells receivers no more values are coming. After close, receives drain any buffered values and then return the zero value immediately; v, ok := <-ch gives ok == false once closed and empty. for v := range ch loops until the channel is closed and drained — the standard consumer loop. Rules:
- Only the sender closes, and only when receivers need to know it's done. Not every channel needs closing.
- Sending on a closed channel panics; closing twice panics.
- A nil channel blocks forever on both send and receive.
Directional types document intent and are enforced by the compiler: chan<- int is send-only, <-chan int receive-only. Functions that produce values typically return <-chan T.
If every goroutine is blocked on channel operations, the runtime aborts with fatal error: all goroutines are asleep - deadlock! — the most common first channel bug, usually a send nobody receives.
Example
package main
import "fmt"
// generate returns a receive-only channel and closes it when done.
func generate(n int) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for i := range n {
out <- i * i
}
}()
return out
}
func main() {
sum := 0
for v := range generate(5) { // ends when out is closed
sum += v
}
fmt.Println(sum) // 30
buf := make(chan string, 2)
buf <- "a"
buf <- "b" // doesn't block: buffer has room
close(buf)
v, ok := <-buf
fmt.Println(v, ok, len(buf)) // a true 1
}Remember
- Unbuffered channels hand off and synchronise; buffered ones queue
- The sender closes; receivers use
rangeorv, ok := <-ch - Use directional types
<-chan T/chan<- Tin signatures
Watch out for
- Closing a channel from the receiver side or from several senders — causes send-on-closed panics.