Sobes.tech
Middle

Parlami dei casi d'uso del contesto in Go.

sobes.tech AI

Risposta dell'AI

Il contesto in Go viene utilizzato per gestire il ciclo di vita delle richieste e controllare l'esecuzione delle goroutine. I principali casi d'uso:

  • Annullamento: Permette di annullare la catena di goroutine associata a una richiesta specifica. Ad esempio, alla chiusura della connessione o in caso di timeout.
  • Timeout: Imposta un termine massimo per l'esecuzione. Se l'operazione non termina entro questo limite, il contesto viene annullato. Questo previene attese infinite.
  • Trasmissione di dati specifici della richiesta: Permette di trasmettere dati relativi a una richiesta specifica (ad esempio, ID utente, identificatore di tracciamento) attraverso la catena di chiamate senza modificarne le firme.

Esempi di utilizzo:

  1. Annullamento tramite segnale:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker(ctx context.Context, id int) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Printf("Lavoratore %d interrotto\n", id)
    			return
    		default:
    			fmt.Printf("Lavoratore %d in funzione...\n", id)
    			time.Sleep(500 * time.Millisecond)
    		}
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	defer cancel() // Garantisce l'annullamento
    
    	go worker(ctx, 1)
    	go worker(ctx, 2)
    
    	time.Sleep(2 * time.Second)
    	cancel() // Annulla il contesto
    
    	time.Sleep(500 * time.Millisecond) // Permette alle goroutine di terminare
    }
    
  2. Timeout:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func longOperation(ctx context.Context) error {
    	select {
    	case <-time.After(3 * time.Second): // Simulazione di un'operazione lunga
    		fmt.Println("Operazione completata con successo")
    		return nil
    	case <-ctx.Done():
    		fmt.Println("Operazione annullata dal contesto:", 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("Errore:", err)
    	}
    }
    
  3. Trasmissione di dati:

    package main
    
    import (
    	"context"
    	"fmt"
    )
    
    type UserKey string
    
    const userIDKey UserKey = "userID"
    
    func processRequest(ctx context.Context) {
    	userID := ctx.Value(userIDKey).(int)
    	fmt.Printf("Elaborazione richiesta per l'ID utente: %d\n", userID)
    	// Ulteriore elaborazione della richiesta con userID
    }
    
    func main() {
    	ctx := context.WithValue(context.Background(), userIDKey, 123)
    	processRequest(ctx)
    }
    

È importante usare context.Background() o context.TODO() come contesto radice e passarli lungo la catena di chiamate. Non si dovrebbe memorizzare il contesto in strutture dati se non è direttamente collegato al ciclo di vita di quella struttura.