Channels
Typed conduits for sending values between goroutines - channels communicate and synchronise.
A channel is a typed conduit for values. Sending a value to a channel and receiving a value from a channel are both synchronisation points - by default, neither completes until the other side is ready.
Create a channel with make(chan T), where T is the type of values it carries. Use <- to send (ch <- value) and receive (value := <-ch). Because sending blocks until a receiver is ready, the send here must happen in a goroutine - otherwise main would freeze waiting for itself.
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 42 // send a value
}()
v := <-ch // receive the value
fmt.Println(v) // 42
}You can pass a channel to any function. Here two goroutines each send a partial sum; main receives both results and adds them. The two receives happen in order, but either goroutine may finish first - channels handle the coordination automatically.
package main
import "fmt"
func sum(s []int, ch chan int) {
total := 0
for _, v := range s {
total += v
}
ch <- total
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
ch := make(chan int)
go sum(s[:len(s)/2], ch) // 7+2+8 = 17
go sum(s[len(s)/2:], ch) // -9+4+0 = -5
x, y := <-ch, <-ch
fmt.Println(x, y, x+y) // -5 17 12 (order may vary)
}Channels can also be used as a done signal - send when a goroutine finishes so the caller knows it is safe to continue:
done := make(chan bool)
go func() {
fmt.Println("working")
done <- true
}()
<-done // wait for goroutine to finish
fmt.Println("done")The arrow direction shows data flow: ch <- v sends v to ch, v := <-ch receives from ch. Receiving from a closed channel returns the zero value immediately with the ok flag set to false:
ch := make(chan int, 1)
ch <- 42
close(ch)
v, ok := <-ch
fmt.Println(v, ok) // 42 true
v, ok = <-ch
fmt.Println(v, ok) // 0 false (channel closed, drained)In production
An unbuffered channel send blocks until a receiver is ready - this is a synchronisation guarantee, not just communication. In tests, channels are the idiomatic way to wait for a goroutine to complete a specific action without polling or time.Sleep. Sending on a closed channel panics; receiving from a closed channel returns the zero value immediately. Never close a channel from the receiver side or from a goroutine that did not create it - use a dedicated closer pattern or sync.Once when multiple producers share one channel.
Enjoyed this? Get more essays on software craft delivered to your inbox.
Subscribe free