Sobes.tech
Middle

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. Verwendet Background und TODO.
  • cancelCtx: Unterstützt Abbruch. Besteht aus einem Basiskontext und einem Abbruchkanal.
  • timerCtx: Unterstützt Frist/Zeitüberschreitung. Enthält cancelCtx und 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.