# Go Context Package nel 2026: Cancellation, Timeout e Domande da Colloquio > Guida completa al package context di Go per gestire cancellation, deadline e valori request-scoped. Esempi pratici e domande frequenti nei colloqui tecnici. - Published: 2026-08-29 - Updated: 2026-08-29 - Author: Anthony Fillion-Maillet - Tags: go, golang, context, concurrency, interview - Reading time: 9 min --- Il package context di Go fornisce il meccanismo standard per trasportare deadline, segnali di cancellation e valori specifici delle request attraverso i confini delle API. Ogni applicazione Go in produzione utilizza context: handler HTTP, query al database, chiamate gRPC e worker in background dipendono tutti da esso per il graceful shutdown e la gestione dei timeout. > **Essenziale per i Colloqui** > > Context è uno degli argomenti più frequentemente richiesti nei colloqui Go. Gli intervistatori si aspettano che i candidati sappiano spiegare la propagazione del context, dimostrare una corretta gestione della cancellation ed evitare errori comuni come la memorizzazione del context nelle struct. ## L'Interfaccia Context e i Suoi Quattro Metodi L'[interfaccia Context](https://pkg.go.dev/context) definisce quattro metodi che ogni implementazione di context deve soddisfare. La comprensione di questi metodi costituisce la base per un utilizzo efficace del context. ```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()` restituisce un channel receive-only. Quando questo channel viene chiuso, ogni goroutine in ascolto riceve immediatamente il segnale. Il pattern `<-ctx.Done()` blocca fino al verificarsi della cancellation. `Err()` spiega il motivo: `context.Canceled` quando cancellato esplicitamente, oppure `context.DeadlineExceeded` quando un timeout o una deadline sono scaduti. ## Creare Context con Background e TODO Due funzioni creano context root: `context.Background()` e `context.TODO()`. Entrambe restituiscono context non-nil e vuoti che non vengono mai cancellati. ```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()` serve come parent per le request in arrivo, funzioni main e codice di inizializzazione. `TODO()` funge da placeholder durante il refactoring quando la sorgente corretta del context non è ancora stata determinata. Gli strumenti di analisi statica possono segnalare l'utilizzo di `TODO()` per revisione successiva. ## Cancellation con WithCancel e WithCancelCause La cancellation manuale permette alle goroutine parent di segnalare alle goroutine figlie che il lavoro deve terminare. Questo pattern appare nei worker pool, nei task in background e nelle implementazioni di graceful shutdown. ```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`, introdotto in Go 1.20, fornisce informazioni di errore più ricche rispetto al semplice `WithCancel`. La funzione `context.Cause()` recupera l'errore passato alla funzione cancel, facilitando il debugging in sistemi complessi. ## Timeout e Deadline per la Gestione delle Request Gli handler HTTP, le chiamate al database e le request a API esterne dovrebbero sempre avere timeout. Il package context offre `WithTimeout` e `WithDeadline` per questo scopo. Entrambi creano context che si cancellano automaticamente quando il tempo scade. ```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) } ``` Il pattern `defer cancel()` garantisce che le risorse vengano rilasciate anche quando l'operazione si completa prima del timeout. Saltare questa chiamata causa un memory leak: la goroutine del timer del context rimane attiva fino alla scadenza del timeout. ## Valori Request-Scoped con WithValue `WithValue` associa dati specifici della request a un context. I casi d'uso comuni includono request ID, token di autenticazione e span di tracing. La [documentazione ufficiale](https://pkg.go.dev/context#WithValue) enfatizza l'uso di tipi personalizzati come chiavi per evitare collisioni. ```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)) } ``` I valori del context formano una catena immutabile: ogni chiamata a `WithValue` crea un nuovo context che avvolge il parent. Le ricerche attraversano questa catena, rendendo i valori profondamente annidati più lenti da accedere. Vanno memorizzati solo dati specifici della request, non configurazioni dell'applicazione o parametri opzionali. ## Utility WithoutCancel e AfterFunc Go 1.21 ha aggiunto `WithoutCancel` per operazioni che devono completarsi indipendentemente dalla cancellation del parent, come i task di cleanup. `AfterFunc` pianifica una callback quando si verifica la cancellation del context. ```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` preserva i valori del context parent ma ignora il suo segnale di cancellation. Questo pattern è adatto per logging, emissione di metriche e commit di database che devono avere successo indipendentemente dall'esito della request. ## Domande e Risposte per Colloqui La preparazione per un colloquio tecnico Go richiede la comprensione degli aspetti interni del context e delle best practice. Queste domande appaiono frequentemente nei colloqui presso aziende che utilizzano Go per servizi backend. Per ulteriore preparazione ai colloqui Go, consultare il modulo [Domande sul Context Package](/technologies/go/interview-questions/context-package). ### Perché context dovrebbe essere il primo parametro? Il team Go ha stabilito questa convenzione per rendere la propagazione del context visibile e consistente. Posizionare context come primo parametro segnala ai lettori che la funzione rispetta la cancellation. La libreria standard segue questo pattern: `http.Request.Context()`, `database/sql.QueryContext()` e `grpc.UnaryInterceptor` si aspettano tutti context come primo argomento. ### Cosa succede se si memorizza context in una struct? Memorizzare context rompe il modello del ciclo di vita della request. Una struct potrebbe sopravvivere alla request per cui è stata creata, causando operazioni che utilizzano segnali di cancellation obsoleti o perdono nuove deadline. La documentazione del context mette esplicitamente in guardia contro questo 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) } ``` ### Come gestire context nelle goroutine? Le goroutine devono controllare `ctx.Done()` nel loro loop principale o nella select statement. Ignorare il channel done crea goroutine leak: la funzione parent ritorna, ma la goroutine continua a consumare risorse. ```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) } } } ``` ### Quando utilizzare context.TODO()? `TODO()` va utilizzato durante il refactoring incrementale quando si aggiunge supporto context a codice legacy. Marca le posizioni che necessitano di una corretta propagazione del context. Il codice in produzione dovrebbe eventualmente sostituire tutte le chiamate `TODO()` con context reali provenienti da handler delle request o inizializzazione dell'applicazione. ## Errori Comuni da Evitare L'uso improprio del context porta a bug sottili che si manifestano sotto carico o durante sequenze di shutdown. Riconoscere questi pattern previene incidenti in produzione. ```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() {} ``` ## Punti Chiave per l'Uso di Go Context in Produzione - Passare context come primo parametro alle funzioni, mai memorizzarlo nelle struct - Chiamare sempre `cancel()` con defer dopo `WithTimeout`, `WithDeadline` o `WithCancel` per prevenire memory leak - Utilizzare `WithCancelCause` e `context.Cause()` per un debugging migliore in catene di cancellation complesse - Controllare `<-ctx.Done()` nei loop delle goroutine per abilitare graceful shutdown e prevenire leak - Usare tipi di chiave personalizzati per `WithValue` per evitare collisioni tra package - Applicare `WithoutCancel` per operazioni di cleanup che devono completarsi indipendentemente dalla cancellation del parent - Preferire `context.Background()` per l'inizializzazione dell'applicazione e `context.TODO()` solo durante il refactoring --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/go/go-context-package-2026