Middle
Tell about cases of using context in Go.
sobes.tech AI
Answer from AI
In Go, context is used to manage the lifecycle of requests and control the execution of goroutines. Main use cases include:
- Cancellation: Allows canceling a chain of goroutines associated with a specific request. For example, when closing a connection or upon timeout.
- Timeout: Sets a deadline for an operation. If the operation does not complete within the deadline, the context is canceled. This prevents infinite waiting.
- Passing request-specific data: Enables passing data related to a specific request (e.g., user ID, trace ID) through the call chain without changing function signatures.
Examples of usage:
-
Cancellation by signal:
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Worker %d interrupted\n", id) return default: fmt.Printf("Worker %d working...\n", id) time.Sleep(500 * time.Millisecond) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Ensure cancellation go worker(ctx, 1) go worker(ctx, 2) time.Sleep(2 * time.Second) cancel() // Cancel the context time.Sleep(500 * time.Millisecond) // Give goroutines time to finish } -
Timeout:
package main import ( "context" "fmt" "time" ) func longOperation(ctx context.Context) error { select { case <-time.After(3 * time.Second): // Simulate long work fmt.Println("Operation completed successfully") return nil case <-ctx.Done(): fmt.Println("Operation cancelled by context:", ctx.Err()) return ctx.Err() } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() err := longOperation(ctx) if err != nil { fmt.Println("Error:", err) } } -
Passing data:
package main import ( "context" "fmt" ) type UserKey string const userIDKey UserKey = "userID" func processRequest(ctx context.Context) { userID := ctx.Value(userIDKey).(int) fmt.Printf("Processing request for user ID: %d\n", userID) // Further request processing using userID } func main() { ctx := context.WithValue(context.Background(), userIDKey, 123) processRequest(ctx) }
It is important to use context.Background() or context.TODO() as the root context and pass it down the call chain. Do not store context in data structures unless it is directly related to the lifecycle of that structure.