Sobes.tech
Middle

Come sono strutturati i contesti nel linguaggio Go?

sobes.tech AI

Risposta dell'AI

Il contesto (context.Context) in Go viene utilizzato per trasmettere informazioni su scadenze, cancellazioni e altri dati tra i confini dell'API. Rappresenta un albero: ogni contesto è figlio di un altro, tranne il radice (context.Background() o context.TODO()).

I metodi principali dell'interfaccia context.Context:

  • Deadline() (deadline time.Time, ok bool): Restituisce la scadenza del contesto.
  • Done() <-chan struct{}: Restituisce un canale che si chiude quando il contesto viene cancellato.
  • Err() error: Restituisce l'errore che ha causato la cancellazione del contesto.
  • Value(key any) any: Restituisce il valore associato alla chiave nel contesto.

Vengono create con funzioni del pacchetto context:

  • context.Background(): Il contesto radice vuoto, usato come base per il processo principale.
  • context.TODO(): Usato come segnaposto quando non si sa quale contesto usare.
  • context.WithCancel(parent Context): Crea un contesto figlio e una funzione di cancellazione.
  • context.WithDeadline(parent Context, d time.Time): Crea un contesto figlio con una scadenza.
  • context.WithTimeout(parent Context, timeout time.Duration): Crea un contesto figlio con un timeout.
  • context.WithValue(parent Context, key, val any): Crea un contesto figlio con un valore.

Implementazioni principali di Context:

  • emptyCtx: Base, non cancellabile e senza valori. Usa Background e TODO.
  • cancelCtx: Supporta la cancellazione. Consiste in un contesto di base e un canale di cancellazione.
  • timerCtx: Supporta scadenza/timeout. Contiene cancelCtx e un timer.
  • valueCtx: Supporta il trasporto di valori. Contiene un contesto di base e coppie chiave-valore.

Quando il contesto genitore viene cancellato, tutti i figli vengono cancellati automaticamente. Questo permette di propagare segnali di cancellazione o scadenze lungo la catena delle chiamate.

Esempio di utilizzo di context.WithCancel:

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// Crea il contesto radice e la funzione di cancellazione
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Garantisce la cancellazione all'uscita da main

	go func() {
		select {
		case <-ctx.Done(): // Attende la cancellazione del contesto
			fmt.Println("Task cancellato:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // Oppure termina in 5 secondi
			fmt.Println("Task completato")
		}
	}()

	// Cancellazione del contesto da un altro punto
	time.Sleep(1 * time.Second)
	fmt.Println("Cancellando il contesto...")
	cancel()

	// Dai tempo alla goroutine di elaborare la cancellazione
	time.Sleep(100 * time.Millisecond)
}

Esempio di utilizzo di context.WithValue:

package main

import (
	"context"
	"fmt"
)

type userIDKey string

func processRequest(ctx context.Context) {
	if userID, ok := ctx.Value(userIDKey("UserID")).(string); ok {
		fmt.Println("Elaborazione richiesta per User ID:", userID)
	} else {
		fmt.Println("User ID non trovato nel contesto")
	}
}

func main() {
	ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
	processRequest(ctx)
}

I contesti sono thread-safe e immutabili, il che è importante per il codice concorrente. Non sono destinati a trasmettere parametri grezzi di funzioni, ma piuttosto a fornire metadati che influenzano l'esecuzione del processo.