thecodex.expert · The Codex Family of Knowledge
Go

select and sync

Channels aren’t the only concurrency tool in Go — select multiplexes channels, and sync.Mutex and sync.WaitGroup handle the cases channels are the wrong fit for.

Go 1.26 sync.WaitGroup Last verified:
Canonical Definition

select is a control structure that blocks until one of several channel operations is ready, choosing randomly if more than one is ready at once — it’s how a goroutine waits on multiple channels simultaneously. The standard library’s sync package covers coordination that channels aren’t suited for: sync.Mutex guards a shared variable from concurrent access, and sync.WaitGroup blocks until a set of goroutines has finished.

select: waiting on multiple channels

Each case in a select is a channel send or receive. select blocks until one case is ready, and a default case makes the whole select non-blocking.

Goselect.go
select {
case msg := <-ch1:
    fmt.Println("from ch1:", msg)
case msg := <-ch2:
    fmt.Println("from ch2:", msg)
case <-time.After(2 * time.Second):
    fmt.Println("timed out")
default:
    fmt.Println("nothing ready right now")   // makes select non-blocking
}

sync.WaitGroup: waiting for goroutines to finish

A WaitGroup counts outstanding goroutines. Add increments the count before launching a goroutine, each goroutine calls Done when finished (typically via defer), and Wait blocks until the count returns to zero.

Gowaitgroup.go
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(n int) {
        defer wg.Done()
        fmt.Println("worker", n, "done")
    }(i)
}
wg.Wait()   // blocks until all 3 workers call Done()
fmt.Println("all workers finished")

sync.Mutex: protecting shared data

Goroutines share memory by default, which means a variable written by multiple goroutines needs protection from concurrent access (a data race). sync.Mutex provides Lock/Unlock to make a critical section run one goroutine at a time; sync.Once guarantees a function runs exactly once no matter how many goroutines call it.

Gomutex.go
var mu sync.Mutex
counter := 0

func increment() {
    mu.Lock()
    defer mu.Unlock()
    counter++   // safe: only one goroutine at a time reaches here
}

Sources

1
The Go Authors. sync package documentation, pkg.go.dev/sync — covers Mutex, WaitGroup, Once, and RWMutex.