Context Package in Go
As Go applications grow larger and more concurrent, managing request lifecycles becomes critical. In this lesson, you will learn about Go’s context package and how it is used to control timeouts, cancellations, and shared values across goroutines.
The context package is heavily used in production systems such as APIs, microservices, and distributed applications.
What Is Context?
The context package provides a way to:
- Cancel operations
- Set deadlines and timeouts
- Pass request-scoped values
Context allows different goroutines working on the same task to communicate when work should stop.
Why Context Is Important
Without context, long-running goroutines may:
- Continue running after a request is cancelled
- Waste CPU and memory
- Cause resource leaks
Context ensures graceful termination of operations when they are no longer needed.
Importing the Context Package
import "context"
Creating a Base Context
Every context starts with a base context.
ctx := context.Background()
context.Background() is typically used in main functions,
initialization, and top-level requests.
Context with Cancellation
You can create a cancellable context using context.WithCancel.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Calling cancel() signals all goroutines using this context to stop.
Using Context in a Goroutine
Here is an example where a goroutine stops when the context is cancelled.
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker stopped:", ctx.Err())
return
default:
fmt.Println("Worker running")
}
}
}
Context with Timeout
You can automatically cancel a context after a specific duration.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
After 2 seconds, the context is cancelled automatically.
Complete Timeout Example
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Task cancelled:", ctx.Err())
return
default:
fmt.Println("Processing...")
time.Sleep(1 * time.Second)
}
}
}(ctx)
time.Sleep(5 * time.Second)
}
Context with Deadline
You can specify an exact time when the context should expire.
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
Passing Values Using Context
Context can carry request-scoped values such as user IDs or request IDs.
ctx := context.WithValue(context.Background(), "userID", 101)
Retrieve values using:
userID := ctx.Value("userID")
Context values should be used sparingly and only for request-level data.
Real-World Use Case: HTTP Request Handling
In web servers, each HTTP request carries a context:
- Client disconnects → context cancelled
- Request timeout → context cancelled
- Downstream services stop work
This prevents unnecessary database calls and API requests.
Best Practices
- Always pass context as the first function parameter
- Do not store contexts in structs
- Do not use context for optional parameters
- Always call cancel functions
Common Mistakes
- Ignoring
ctx.Done() - Forgetting to cancel contexts
- Using context as a global variable
Practice Exercises
Exercise 1
Create a context with a 2-second timeout and stop a goroutine using it.
Exercise 2
Pass a request ID using context and print it inside a goroutine.
Key Takeaways
- Context manages cancellation and timeouts
- Essential for concurrent and distributed systems
- Prevents resource leaks
- Used extensively in production Go code
What’s Next?
In the next lesson, you will learn about Concurrency Patterns and how to design robust concurrent systems in Go.