Using Context in Go: Cancellation, Timeout, and Deadlines Explained
As your Go applications become more concurrent and complex, you'll need a way to manage the lifecycle of your goroutines—especially when you want to cancel them, set timeouts, or propagate deadlines. This is where the context
package comes in. It's the idiomatic way in Go to control concurrent processes gracefully and reliably.
In this article, you’ll learn:
- What
context
is and why it’s important - Using
context.Background()
andcontext.TODO()
- How to cancel a goroutine with
context.WithCancel()
- How to set a timeout or deadline
- How to check if a context is done
- Real-world examples and best practices
What Is Context?
The context
package provides a way to carry deadlines, cancellation signals, and other request-scoped values across function boundaries and between goroutines.
It helps you:
- Cancel long-running tasks
- Set deadlines or timeouts
- Propagate cancellation across multiple goroutines
Starting Point: Background and TODO
ctx := context.Background() // root context, no cancel/timeout
ctx := context.TODO() // use when unsure (placeholder)
Cancelling a Goroutine: WithCancel
You can use context.WithCancel
to manually stop a goroutine:
func doWork(ctx context.Context) {
for {
select {
case <-ctx .done="" :="context.WithCancel(context.Background())" cancel="" canceled="" code="" context="" ctx="" default:="" dowork="" fmt.println="" func="" go="" main="" orking...="" oroutine="" return="" the="" time.millisecond="" time.second="" time.sleep="">-ctx>
When cancel()
is called, the goroutine receives a signal via ctx.Done()
.
Setting a Timeout: WithTimeout
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time .after="" case="" code="" completed="" ctx.done="" ctx.err="" fmt.println="" ontext="" peration="" time.second="" timeout:="">-time>
WithDeadline
works the same way, but with a fixed time:
deadline := time.Now().Add(2 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
How to Use ctx.Done()
The ctx.Done()
channel is closed when the context is canceled or times out. Use it in select
blocks to exit early.
Real-World Example: HTTP Request Timeout
func fetch(ctx context.Context, url string) error {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Println("Status:", resp.Status)
return nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
err := fetch(ctx, "https://httpbin.org/delay/2")
if err != nil {
fmt.Println("Request failed:", err)
}
}
Best Practices
- Always call
cancel()
to release resources - Pass
context.Context
as the first argument in your functions - Use
context.WithTimeout
for operations with time limits - Use
context.WithCancel
for manual control
Common Mistakes
- Not deferring
cancel()
→ memory leak - Ignoring
ctx.Err()
→ silent failure - Passing nil context or using
context.TODO()
in production
Conclusion
Understanding context
is essential for writing responsive, well-behaved concurrent programs in Go. Whether you're managing goroutines, dealing with timeouts, or handling request chains in a web server, context gives you the tools to do it cleanly and safely.
Next, we’ll cover sync.Mutex
and other tools for synchronizing data between goroutines.
Happy coding!
Post a Comment for "Using Context in Go: Cancellation, Timeout, and Deadlines Explained"