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.

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.
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 definiert vier Methoden, die jede Context-Implementierung erfüllen muss. Das Verständnis dieser Methoden bildet die Grundlage für effektive Context-Nutzung.
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.
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.
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.
Bereit für deine Go-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
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.
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 betont die Verwendung benutzerdefinierter Typen als Keys, um Kollisionen zu vermeiden.
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.
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 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.
// 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.
// 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.
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() {}Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
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()nachWithTimeout,WithDeadlineoderWithCancelmit defer aufrufen, um Ressourcenlecks zu verhindern WithCancelCauseundcontext.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
WithValueverwenden, um Kollisionen zwischen Packages zu vermeiden WithoutCancelfür Cleanup-Operationen anwenden, die unabhängig von der Parent-Cancellation abgeschlossen werden müssencontext.Background()für Anwendungsinitialisierung bevorzugen undcontext.TODO()nur während des Refactorings verwenden
Findest du den Bug in Go?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 29. August 2026
Tags
Teilen
Verwandte Artikel

Top 25 Go-Interviewfragen: vollständiger Entwicklerleitfaden
Go-Interviews meistern mit den 25 häufigsten Fragen. Goroutinen, Channels, Schnittstellen und Concurrency-Muster mit Codebeispielen.

Go-Interview: Goroutines, Channels und Concurrency-Patterns meistern
Go-Interviewfragen zu Goroutines, Channels und Concurrency mit Codebeispielen. Fan-Out/Fan-In, Worker Pools, Race Conditions und Context-Patterns.

Nebenläufigkeit in Go: Goroutinen und Kanäle - Vollständiger Leitfaden
Beherrschen Sie die Nebenläufigkeit in Go mit Goroutinen und Kanälen. Fortgeschrittene Muster, Synchronisation, select-Anweisungen und Best Practices mit detaillierten Codebeispielen.