Sobes.tech
Middle

Erzählen Sie mir von Anwendungsfällen des Kontexts in Go.

sobes.tech KI

Antwort von AI

Der Kontext in Go wird verwendet, um den Lebenszyklus von Anfragen zu verwalten und die Ausführung von Goroutines zu steuern. Die Hauptanwendungsfälle:

  • Abbruch: Ermöglicht das Abbrechen der Kette von Goroutines, die mit einer bestimmten Anfrage verbunden sind. Zum Beispiel beim Schließen der Verbindung oder bei Timeout.
  • Timeout: Legt eine Frist für die Ausführung fest. Wenn die Operation innerhalb dieser Frist nicht abgeschlossen wird, wird der Kontext abgebrochen. Dies verhindert unendliche Wartezeiten.
  • Weitergabe von anfrage-spezifischen Daten: Ermöglicht das Weitergeben von Daten, die sich auf eine bestimmte Anfrage beziehen (z.B. Benutzer-ID, Trace-Identifikator), durch die Aufrufkette, ohne deren Signaturen zu ändern.

Anwendungsbeispiele:

  1. Abbruch durch Signal:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func worker(ctx context.Context, id int) {
    	for {
    		select {
    		case <-ctx.Done():
    			fmt.Printf("Arbeiter %d unterbrochen\n", id)
    			return
    		default:
    			fmt.Printf("Arbeiter %d arbeitet...\n", id)
    			time.Sleep(500 * time.Millisecond)
    		}
    	}
    }
    
    func main() {
    	ctx, cancel := context.WithCancel(context.Background())
    	defer cancel() // Gewährleistet Abbruch
    
    	go worker(ctx, 1)
    	go worker(ctx, 2)
    
    	time.Sleep(2 * time.Second)
    	cancel() // Kontext abbrechen
    
    	time.Sleep(500 * time.Millisecond) // Zeit für Goroutines zum Beenden
    }
    
  2. Timeout:

    package main
    
    import (
    	"context"
    	"fmt"
    	"time"
    )
    
    func longOperation(ctx context.Context) error {
    	select {
    	case <-time.After(3 * time.Second): // Simulation einer langen Operation
    		fmt.Println("Operation erfolgreich abgeschlossen")
    		return nil
    	case <-ctx.Done():
    		fmt.Println("Operation vom Kontext abgebrochen:", 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("Fehler:", err)
    	}
    }
    
  3. Weitergabe von Daten:

    package main
    
    import (
    	"context"
    	"fmt"
    )
    
    type UserKey string
    
    const userIDKey UserKey = "userID"
    
    func processRequest(ctx context.Context) {
    	userID := ctx.Value(userIDKey).(int)
    	fmt.Printf("Verarbeite Anfrage für Benutzer-ID: %d\n", userID)
    	// Weiterverarbeitung der Anfrage mit userID
    }
    
    func main() {
    	ctx := context.WithValue(context.Background(), userIDKey, 123)
    	processRequest(ctx)
    }
    

Es ist wichtig, context.Background() oder context.TODO() als Wurzelkontext zu verwenden und ihn nach unten in die Aufrufkette zu übergeben. Der Kontext sollte nicht in Datenstrukturen gespeichert werden, wenn er nicht direkt mit dem Lebenszyklus dieser Struktur verbunden ist.