Wie sind die Kontexte in der Programmiersprache Go aufgebaut?
sobes.tech KI
Antwort von AI
Der (context.Context) in Go wird verwendet, um Informationen über Fristen, Abbrüche und andere Daten zwischen API-Grenzen zu übertragen. Er stellt einen Baum dar: Jeder Kontext ist ein Kind eines anderen, außer dem Wurzel (context.Background() oder context.TODO()).
Die Hauptmethoden der Schnittstelle context.Context:
Deadline() (deadline time.Time, ok bool): Gibt die Frist des Kontexts zurück.Done() <-chan struct{}: Gibt einen Kanal zurück, der geschlossen wird, wenn der Kontext abgebrochen wird.Err() error: Gibt den Fehler zurück, der zum Abbruch des Kontexts geführt hat.Value(key any) any: Gibt den mit dem Schlüssel im Kontext verbundenen Wert zurück.
Sie werden mit Funktionen des Pakets context erstellt:
context.Background(): Der leere Wurzelkontext, der als Basis für den Hauptprozess dient.context.TODO(): Wird als Platzhalter verwendet, wenn nicht bekannt ist, welchen Kontext man verwenden soll.context.WithCancel(parent Context): Erstellt einen Kindkontext und eine Abbruchfunktion.context.WithDeadline(parent Context, d time.Time): Erstellt einen Kindkontext mit einer Frist.context.WithTimeout(parent Context, timeout time.Duration): Erstellt einen Kindkontext mit einer Zeitüberschreitung.context.WithValue(parent Context, key, val any): Erstellt einen Kindkontext mit einem Wert.
Hauptimplementierungen von Context:
emptyCtx: Grundlegend, nicht abbrufbar und ohne Werte. VerwendetBackgroundundTODO.cancelCtx: Unterstützt Abbruch. Besteht aus einem Basiskontext und einem Abbruchkanal.timerCtx: Unterstützt Frist/Zeitüberschreitung. EnthältcancelCtxund einen Timer.valueCtx: Unterstützt die Weitergabe von Werten. Enthält einen Basiskontext und Schlüssel-Wert-Paare.
Beim Abbruch des Elternkontexts werden alle Kindkontexte automatisch abgebrochen. Dies ermöglicht die Weiterleitung von Abbruchsignalen oder Fristen in der Kette der Aufrufe.
Beispiel für die Verwendung von context.WithCancel:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Erstellen des Wurzelkontexts und der Abbruchfunktion
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Gewährleistet den Abbruch beim Verlassen von main
go func() {
select {
case <-ctx.Done(): // Warten auf Abbruch des Kontexts
fmt.Println("Aufgabe abgebrochen:", ctx.Err())
return
case <-time.After(5 * time.Second): // Oder nach 5 Sekunden
fmt.Println("Aufgabe abgeschlossen")
}
}()
// Abbruch des Kontexts von einer anderen Stelle
time.Sleep(1 * time.Second)
fmt.Println("Abbruch des Kontexts...")
cancel()
// Warten, damit die Goroutine den Abbruch verarbeitet
time.Sleep(100 * time.Millisecond)
}
Beispiel für die Verwendung von 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("Verarbeitung der Anfrage für User ID:", userID)
} else {
fmt.Println("User ID im Kontext nicht gefunden")
}
}
func main() {
ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
processRequest(ctx)
}
Kontexte sind threadsicher und unveränderlich, was für konkurrierenden Code wichtig ist. Sie sind nicht dafür gedacht, unverarbeitete Parameter von Funktionen zu übertragen, sondern eher für Metainformationen, die die Ausführung des Prozesses beeinflussen.