How to Use Context for Cancellation in Go: Simple Guide
In Go, use the
context package to manage cancellation by creating a cancellable context with context.WithCancel. Pass this context to goroutines or functions, and call the cancel function to stop work early, which they detect by checking ctx.Done().Syntax
The basic pattern for cancellation uses context.WithCancel(parentContext), which returns a new context and a cancel function. You pass the new context to your goroutines or functions. When you want to stop the work, call the cancel function. The goroutines listen for cancellation by checking the ctx.Done() channel.
parentContext: Usuallycontext.Background()or another context.ctx: The new cancellable context.cancel: Function to call to cancel the context.ctx.Done(): Channel closed when cancellation happens.
go
ctx, cancel := context.WithCancel(context.Background()) // pass ctx to goroutines // call cancel() to stop select { case <-ctx.Done(): // handle cancellation }
Example
This example shows a goroutine that prints numbers every second. The main function cancels the context after 3 seconds, stopping the goroutine cleanly.
go
package main import ( "context" "fmt" "time" ) func printNumbers(ctx context.Context) { i := 1 for { select { case <-ctx.Done(): fmt.Println("Cancelled, stopping printNumbers") return default: fmt.Println(i) i++ time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go printNumbers(ctx) time.Sleep(3 * time.Second) cancel() // stop the goroutine // wait a bit to see cancellation message time.Sleep(1 * time.Second) }
Output
1
2
3
Cancelled, stopping printNumbers
Common Pitfalls
Common mistakes include:
- Not passing the context to all goroutines or functions that need cancellation.
- Forgetting to call the cancel function, which can cause resource leaks.
- Ignoring the
ctx.Done()channel, so goroutines never stop. - Using
context.Background()directly inside goroutines instead of passing the cancellable context.
Always ensure you call the cancel function and check ctx.Done() properly.
go
/* Wrong way: ignoring cancellation */ func work() { for { // no ctx, no cancellation check fmt.Println("Working...") time.Sleep(1 * time.Second) } } /* Right way: use context and check ctx.Done() */ func work(ctx context.Context) { for { select { case <-ctx.Done(): return default: fmt.Println("Working...") time.Sleep(1 * time.Second) } } }
Quick Reference
Remember these key points when using context for cancellation:
- Use
context.WithCancel(parent)to create a cancellable context. - Pass the context to all functions and goroutines that should stop on cancellation.
- Call the cancel function to signal cancellation.
- Check
ctx.Done()channel to detect cancellation and stop work. - Always call cancel to avoid resource leaks.
Key Takeaways
Use context.WithCancel to create a cancellable context and get a cancel function.
Pass the cancellable context to all goroutines or functions that need to stop early.
Call the cancel function to signal cancellation and have goroutines listen on ctx.Done().
Always check ctx.Done() in select statements to handle cancellation properly.
Remember to call cancel to avoid resource leaks and clean up resources.