Middle
Opowiedz o przypadkach użycia kontekstu w Go.
sobes.tech AI
Odpowiedź od AI
Kontext w Go jest używany do zarządzania cyklem życia żądań i kontroli wykonywania gorutyn. Główne przypadki użycia:
- Anulowanie: Pozwala na anulowanie łańcucha gorutyn związanych z określonym żądaniem. Na przykład przy zamykaniu połączenia lub z powodu limitu czasowego.
- Limit czasowy: Ustawia ostateczny termin wykonania operacji. Jeśli operacja nie zakończy się w tym czasie, kontekst jest anulowany. Zapobiega to nieskończonemu oczekiwaniu.
- Przekazywanie danych specyficznych dla żądania: Pozwala na przekazywanie danych związanych z konkretnym żądaniem (np. ID użytkownika, identyfikator śledzenia) przez łańcuch wywołań bez zmiany ich sygnatur.
Przykłady użycia:
-
Anulowanie za pomocą sygnału:
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Pracownik %d przerwany\n", id) return default: fmt.Printf("Pracownik %d pracuje...\n", id) time.Sleep(500 * time.Millisecond) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Gwarantuje anulowanie go worker(ctx, 1) go worker(ctx, 2) time.Sleep(2 * time.Second) cancel() // Anuluje kontekst time.Sleep(500 * time.Millisecond) // Daje czas gorutynom na zakończenie } -
Limit czasowy:
package main import ( "context" "fmt" "time" ) func longOperation(ctx context.Context) error { select { case <-time.After(3 * time.Second): // Symulacja długiej operacji fmt.Println("Operacja zakończona pomyślnie") return nil case <-ctx.Done(): fmt.Println("Operacja anulowana przez kontekst:", 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("Błąd:", err) } } -
Przekazywanie danych:
package main import ( "context" "fmt" ) type UserKey string const userIDKey UserKey = "userID" func processRequest(ctx context.Context) { userID := ctx.Value(userIDKey).(int) fmt.Printf("Przetwarzanie żądania dla ID użytkownika: %d\n", userID) // Dalsza obsługa żądania z użyciem userID } func main() { ctx := context.WithValue(context.Background(), userIDKey, 123) processRequest(ctx) }
Ważne jest używanie context.Background() lub context.TODO() jako kontekstu głównego i przekazywanie go w dół łańcucha wywołań. Nie należy przechowywać kontekstu w strukturach danych, jeśli nie jest on bezpośrednio związany z cyklem życia tej struktury.