Concurrency · 3 MIN READ
select waits on several things; context says stop
Make a blocking operation cancellable without a flag or a kill switch.
select blocks until one of its cases can proceed, and picks randomly among those that are ready. Adding a case for <-ctx.Done() turns any wait into a cancellable one.
The convention that makes this composable: context is the first parameter, it is passed rather than stored, and cancel is always deferred so the resources behind it are released on every path.
SEE THE IDEA
Wait for work and cancellation together
- 01
Work becomes ready
gocase v := <-values:Continue with a received value
- 02
Cancellation
gocase <-ctx.Done():Return the context error
- 03
Neither ready
goselect { ... }The goroutine waits
If several cases can proceed, select chooses one; source order is not a priority rule.
In practice
Make every potentially blocking send or receive part of the cancellation design. A context is a signal, so the operation must actually observe it. When a channel may close, use the receive boolean to distinguish the end of the stream from a real zero-valued item.
Watch out
Cancellation does not forcibly stop a goroutine. Work that never checks ctx.Done can keep running.
Work through the exercise
package main
import (
"context"
"errors"
)
// waitFor should return ctx.Err() when the context is cancelled
// before a value arrives.
func waitFor(ctx context.Context, values <-chan int) (int, error) {
return <-values, nil
}
func main() { _ = errors.New }Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.
Progress stays in this browser. No account needed.