# Go Context Package 2026: Cancellation, Timeouts und Interview-Fragen > Umfassender Leitfaden zum Go context Package für Cancellation, Deadlines und request-scoped Values. Praktische Beispiele und häufige Interview-Fragen. - Published: 2026-08-29 - Updated: 2026-08-29 - Author: Anthony Fillion-Maillet - Tags: go, golang, context, concurrency, interview - Reading time: 9 min --- Das Go context Package stellt den Standardmechanismus bereit, um Deadlines, Cancellation-Signale und Request-spezifische Werte über API-Grenzen hinweg zu transportieren. Jede produktionsreife Go-Anwendung verwendet context: HTTP-Handler, Datenbank-Queries, gRPC-Aufrufe und Background-Worker sind alle darauf angewiesen für Graceful Shutdown und Timeout-Management. > **Interview-Essentials** > > Context gehört zu den am häufigsten gefragten Themen in Go-Interviews. Interviewer erwarten von Kandidaten, dass sie Context-Propagation erklären, korrektes Cancellation-Handling demonstrieren und häufige Fallstricke wie das Speichern von Context in Structs vermeiden können. ## Das Context Interface und seine vier Methoden Das [Context Interface](https://pkg.go.dev/context) definiert vier Methoden, die jede Context-Implementierung erfüllen muss. Das Verständnis dieser Methoden bildet die Grundlage für effektive Context-Nutzung. ```go // context_interface.go type Context interface { // Deadline returns the time when work should be canceled Deadline() (deadline time.Time, ok bool) // Done returns a channel that closes when the context is canceled Done() <-chan struct{} // Err returns the reason why Done was closed Err() error // Value returns the value associated with key, or nil Value(key any) any } ``` `Done()` gibt einen Receive-Only-Channel zurück. Wenn dieser Channel geschlossen wird, erhält jede Goroutine, die darauf wartet, sofort das Signal. Das Pattern `<-ctx.Done()` blockiert bis zur Cancellation. `Err()` erklärt den Grund: entweder `context.Canceled` bei expliziter Cancellation oder `context.DeadlineExceeded` wenn ein Timeout oder eine Deadline überschritten wurde. ## Contexts erstellen mit Background und TODO Zwei Funktionen erstellen Root-Contexts: `context.Background()` und `context.TODO()`. Beide geben nicht-nil, leere Contexts zurück, die niemals gecanceled werden. ```go // context_creation.go package main import ( "context" "log" "net/http" ) func main() { // Background: the root context for your application ctx := context.Background() // Use it as parent for derived contexts server := &http.Server{Addr: ":8080"} // TODO: placeholder when context source is unclear // Static analysis tools can flag context.TODO() for review processLegacyData(context.TODO()) } func processLegacyData(ctx context.Context) { // Context parameter enables future cancellation support log.Println("Processing data...") } ``` `Background()` dient als Parent für eingehende Requests, main-Funktionen und Initialisierungscode. `TODO()` fungiert als Platzhalter während des Refactorings, wenn die korrekte Context-Quelle noch nicht feststeht. Statische Analysetools können `TODO()`-Verwendungen für spätere Überprüfung markieren. ## Cancellation mit WithCancel und WithCancelCause Manuelle Cancellation ermöglicht es Parent-Goroutines, Child-Goroutines zu signalisieren, dass die Arbeit beendet werden soll. Dieses Pattern erscheint in Worker-Pools, Background-Tasks und Graceful-Shutdown-Implementierungen. ```go // cancellation.go package main import ( "context" "errors" "fmt" "time" ) func worker(ctx context.Context, id int, results chan<- int) { for { select { case <-ctx.Done(): // Check why cancellation occurred if cause := context.Cause(ctx); cause != nil { fmt.Printf("Worker %d stopped: %v\n", id, cause) } return default: // Simulate work time.Sleep(100 * time.Millisecond) results <- id * 10 } } } func main() { // WithCancelCause provides error context ctx, cancel := context.WithCancelCause(context.Background()) results := make(chan int, 10) // Start 3 workers for i := 1; i <= 3; i++ { go worker(ctx, i, results) } // Collect some results for i := 0; i < 5; i++ { fmt.Println("Result:", <-results) } // Cancel with a specific reason cancel(errors.New("shutdown requested by user")) // context.Cause retrieves the cancellation reason time.Sleep(50 * time.Millisecond) fmt.Println("Cause:", context.Cause(ctx)) } ``` `WithCancelCause`, eingeführt in Go 1.20, liefert reichhaltigere Fehlerinformationen als das einfache `WithCancel`. Die Funktion `context.Cause()` ruft den Fehler ab, der an die Cancel-Funktion übergeben wurde, was das Debugging in komplexen Systemen erleichtert. ## Timeouts und Deadlines für Request-Handling HTTP-Handler, Datenbank-Aufrufe und externe API-Requests sollten immer Timeouts haben. Das context Package bietet `WithTimeout` und `WithDeadline` für diesen Zweck. Beide erstellen Contexts, die automatisch canceln, wenn die Zeit abläuft. ```go // timeouts.go package main import ( "context" "fmt" "time" ) // fetchFromAPI simulates an HTTP call func fetchFromAPI(ctx context.Context, endpoint string) (string, error) { // Simulate variable response time responseTime := time.Duration(100+endpoint[0]%150) * time.Millisecond select { case <-time.After(responseTime): return fmt.Sprintf("Response from %s", endpoint), nil case <-ctx.Done(): return "", ctx.Err() } } func main() { // WithTimeout: relative duration ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) defer cancel() // Always call cancel to release resources result, err := fetchFromAPI(ctx, "api.example.com/users") if err != nil { if err == context.DeadlineExceeded { fmt.Println("Request timed out") } else { fmt.Println("Request canceled") } return } fmt.Println(result) // WithDeadline: absolute time deadline := time.Now().Add(500 * time.Millisecond) ctx2, cancel2 := context.WithDeadline(context.Background(), deadline) defer cancel2() result2, _ := fetchFromAPI(ctx2, "api.example.com/orders") fmt.Println(result2) } ``` Das `defer cancel()`-Pattern stellt sicher, dass Ressourcen freigegeben werden, auch wenn die Operation vor dem Timeout abgeschlossen wird. Das Auslassen dieses Aufrufs verursacht ein Ressourcenleck: Die Timer-Goroutine des Contexts bleibt aktiv, bis das Timeout abläuft. ## Request-spezifische Werte mit WithValue `WithValue` hängt request-spezifische Daten an einen Context an. Häufige Anwendungsfälle umfassen Request-IDs, Authentifizierungs-Tokens und Tracing-Spans. Die [offizielle Dokumentation](https://pkg.go.dev/context#WithValue) betont die Verwendung benutzerdefinierter Typen als Keys, um Kollisionen zu vermeiden. ```go // context_values.go package main import ( "context" "fmt" "log" "net/http" ) // Define custom key types to avoid collisions type contextKey string const ( requestIDKey contextKey = "requestID" userIDKey contextKey = "userID" ) // middleware adds request ID to context func requestIDMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestID := r.Header.Get("X-Request-ID") if requestID == "" { requestID = generateRequestID() } // Create new context with request ID ctx := context.WithValue(r.Context(), requestIDKey, requestID) next.ServeHTTP(w, r.WithContext(ctx)) }) } // handler retrieves values from context func handler(w http.ResponseWriter, r *http.Request) { requestID, ok := r.Context().Value(requestIDKey).(string) if !ok { requestID = "unknown" } log.Printf("[%s] Processing request", requestID) fmt.Fprintf(w, "Request ID: %s", requestID) } func generateRequestID() string { return fmt.Sprintf("req-%d", time.Now().UnixNano()) } import "time" func main() { mux := http.NewServeMux() mux.HandleFunc("/", handler) http.ListenAndServe(":8080", requestIDMiddleware(mux)) } ``` Context-Werte bilden eine unveränderliche Kette: Jeder `WithValue`-Aufruf erstellt einen neuen Context, der den Parent umschließt. Lookups durchlaufen diese Kette, wodurch tief verschachtelte Werte langsamer zugänglich sind. Es sollten nur request-spezifische Daten gespeichert werden, keine Anwendungskonfiguration oder optionale Parameter. ## WithoutCancel und AfterFunc Utilities Go 1.21 fügte `WithoutCancel` hinzu für Operationen, die unabhängig von der Parent-Cancellation abgeschlossen werden müssen, wie Cleanup-Tasks. `AfterFunc` plant einen Callback, wenn Context-Cancellation auftritt. ```go // utilities.go package main import ( "context" "fmt" "log" "time" ) func saveAuditLog(ctx context.Context, message string) error { // Use WithoutCancel: audit logs must complete even if request canceled cleanCtx := context.WithoutCancel(ctx) // Simulate database write select { case <-time.After(50 * time.Millisecond): log.Printf("Audit: %s", message) return nil case <-cleanCtx.Done(): // This branch never executes: WithoutCancel context is never canceled return cleanCtx.Err() } } func main() { ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) // AfterFunc: register cleanup when context ends stop := context.AfterFunc(ctx, func() { fmt.Println("Context ended, running cleanup") }) // Perform operation time.Sleep(50 * time.Millisecond) // Cancel stops the AfterFunc from running if called before context ends if stop() { fmt.Println("Cleanup prevented") } cancel() // Audit log completes despite parent cancellation saveAuditLog(ctx, "Operation completed") } ``` `WithoutCancel` bewahrt die Werte des Parent-Contexts, ignoriert aber dessen Cancellation-Signal. Dieses Pattern eignet sich für Logging, Metrik-Emissionen und Datenbank-Commits, die unabhängig vom Request-Ergebnis erfolgreich sein müssen. ## Interview-Fragen und Antworten Die Vorbereitung auf ein technisches Go-Interview erfordert das Verständnis von Context-Interna und Best Practices. Diese Fragen tauchen häufig in Interviews bei Unternehmen auf, die Go für Backend-Services nutzen. Weitere Go-Interview-Vorbereitung findet sich im [Context Package Interview-Fragen](/technologies/go/interview-questions/context-package) Modul. ### Warum sollte context der erste Parameter sein? Das Go-Team etablierte diese Konvention, um Context-Propagation sichtbar und konsistent zu machen. Context als ersten Parameter zu platzieren signalisiert Lesern, dass die Funktion Cancellation respektiert. Die Standardbibliothek folgt diesem Muster: `http.Request.Context()`, `database/sql.QueryContext()` und `grpc.UnaryInterceptor` erwarten alle context als erstes Argument. ### Was passiert, wenn Context in einem Struct gespeichert wird? Das Speichern von Context bricht das Request-Lifecycle-Modell. Ein Struct könnte den Request überleben, für den es erstellt wurde, was dazu führt, dass Operationen veraltete Cancellation-Signale verwenden oder neue Deadlines verpassen. Die Context-Dokumentation warnt explizit vor diesem Pattern. ```go // BAD: context stored in struct type Service struct { ctx context.Context // Never do this } // GOOD: pass context to each method type Service struct{} func (s *Service) Process(ctx context.Context, data []byte) error { // Context flows through the call chain return s.save(ctx, data) } ``` ### Wie behandelt man Context in Goroutines? Goroutines müssen `ctx.Done()` in ihrer Hauptschleife oder Select-Anweisung prüfen. Das Ignorieren des Done-Channels erzeugt Goroutine-Leaks: Die Parent-Funktion kehrt zurück, aber die Goroutine verbraucht weiterhin Ressourcen. ```go // Correct pattern for long-running goroutines func processStream(ctx context.Context, stream <-chan Data) { for { select { case <-ctx.Done(): log.Println("Shutting down processor") return case data, ok := <-stream: if !ok { return } handle(data) } } } ``` ### Wann sollte context.TODO() verwendet werden? `TODO()` sollte während inkrementellem Refactoring verwendet werden, wenn Context-Unterstützung zu Legacy-Code hinzugefügt wird. Es markiert Stellen, die ordnungsgemäße Context-Propagation benötigen. Produktionscode sollte schließlich alle `TODO()`-Aufrufe durch echte Contexts von Request-Handlern oder Anwendungsinitialisierung ersetzen. ## Häufige Fehler, die vermieden werden sollten Context-Missbrauch führt zu subtilen Bugs, die sich unter Last oder während Shutdown-Sequenzen manifestieren. Das Erkennen dieser Patterns verhindert Produktionsvorfälle. ```go // mistakes.go package main import ( "context" "time" ) // MISTAKE 1: Ignoring context cancellation func badWorker(ctx context.Context) { for { // Missing select on ctx.Done() doExpensiveWork() // Never stops when context canceled } } // MISTAKE 2: Not calling cancel func leakyTimeout() { ctx, _ := context.WithTimeout(context.Background(), time.Second) // Timer goroutine leaks until timeout expires _ = ctx } // MISTAKE 3: Using string keys for values func collisionProne(ctx context.Context) context.Context { // Different packages might use same string key return context.WithValue(ctx, "userID", 123) // Bad } // CORRECT versions func goodWorker(ctx context.Context) { for { select { case <-ctx.Done(): return default: doExpensiveWork() } } } func properTimeout() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() // Always release resources _ = ctx } type userIDKey struct{} func collisionSafe(ctx context.Context) context.Context { return context.WithValue(ctx, userIDKey{}, 123) // Good } func doExpensiveWork() {} ``` ## Wichtige Erkenntnisse für die Verwendung von Go Context in der Produktion - Context als ersten Parameter an Funktionen übergeben, niemals in Structs speichern - Immer `cancel()` nach `WithTimeout`, `WithDeadline` oder `WithCancel` mit defer aufrufen, um Ressourcenlecks zu verhindern - `WithCancelCause` und `context.Cause()` für besseres Error-Debugging in komplexen Cancellation-Ketten verwenden - `<-ctx.Done()` in Goroutine-Schleifen prüfen, um Graceful Shutdown zu ermöglichen und Leaks zu verhindern - Benutzerdefinierte Key-Typen für `WithValue` verwenden, um Kollisionen zwischen Packages zu vermeiden - `WithoutCancel` für Cleanup-Operationen anwenden, die unabhängig von der Parent-Cancellation abgeschlossen werden müssen - `context.Background()` für Anwendungsinitialisierung bevorzugen und `context.TODO()` nur während des Refactorings verwenden --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/go/go-context-package-2026