Go by Example

Channel Synchronization

Use channels as done signals to wait for goroutines to finish before continuing.

Channels can synchronize execution across goroutines. A goroutine signals it is done by sending a value (or closing) a channel - the main goroutine blocks on a receive until the signal arrives.

Launch a goroutine that does some work and signals completion via a done channel. The main goroutine blocks on <-done until the worker sends.

package main
 
import (
    "fmt"
    "time"
)
 
func worker(done chan bool) {
    fmt.Println("working...")
    time.Sleep(time.Second)
    fmt.Println("done")
    done <- true
}
 
func main() {
    done := make(chan bool)
    go worker(done)
    <-done // block until worker signals
}

When one goroutine needs to signal many others at once (called fan-out), close the channel instead of sending N values. A close broadcasts to all receivers.

done := make(chan struct{})
 
for i := 0; i < 3; i++ {
    go func(id int) {
        fmt.Printf("worker %d started\n", id)
        <-done // each worker waits for the close
        fmt.Printf("worker %d shutting down\n", id)
    }(i)
}
 
time.Sleep(100 * time.Millisecond)
close(done) // broadcast to all three goroutines
time.Sleep(100 * time.Millisecond)

For joining N goroutines each doing independent work, collecting results via a channel can replace sync.WaitGroup.

results := make(chan int, 5) // buffered so goroutines don't block
 
for i := 1; i <= 5; i++ {
    go func(n int) {
        results <- n * n
    }(i)
}
 
sum := 0
for i := 0; i < 5; i++ {
    sum += <-results
}
fmt.Println("sum of squares:", sum) // 55

In production

The done channel pattern (close a chan struct{} to broadcast cancellation to multiple goroutines) predates the context package and is still common in library code. Prefer context.Context in new application code - it composes cancellation, deadlines, and values in one type that the whole standard library understands. chan struct{} uses zero memory per item and signals intent clearly: this channel carries no data, only timing.

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

Subscribe free