Sobes.tech
Middle

Jak jsou strukturovány kontexty v jazyce Go?

sobes.tech AI

Odpověď od AI

context (context.Context) v Go se používá k přenosu informací o časových limitech, zrušeních a dalších údajích mezi API hranicemi. Představuje strom: každý kontext je potomkem jiného, kromě kořenového (context.Background() nebo context.TODO()).

Hlavní metody rozhraní context.Context:

  • Deadline() (deadline time.Time, ok bool): Vrací časový limit kontextu.
  • Done() <-chan struct{}: Vrací kanál, který se uzavře při zrušení kontextu.
  • Err() error: Vrací chybu, která vedla ke zrušení kontextu.
  • Value(key any) any: Vrací hodnotu spojenou s klíčem v kontextu.

Vytváří se pomocí funkcí balíčku context:

  • context.Background(): Kořenový prázdný kontext, používá se jako základ hlavního procesu.
  • context.TODO(): Používá se jako dočasná náhrada, když není známo, jaký kontext použít.
  • context.WithCancel(parent Context): Vytváří podřízený kontext a funkci zrušení.
  • context.WithDeadline(parent Context, d time.Time): Vytváří podřízený kontext s časovým limitem.
  • context.WithTimeout(parent Context, timeout time.Duration): Vytváří podřízený kontext s časovým limitem.
  • context.WithValue(parent Context, key, val any): Vytváří podřízený kontext s hodnotou.

Hlavní implementace Context:

  • emptyCtx: Základní, nezrušitelný a bez hodnot. Používá se Background a TODO.
  • cancelCtx: Podporuje zrušení. Skládá se ze základního kontextu a kanálu zrušení.
  • timerCtx: Podporuje časový limit / časovač. Obsahuje cancelCtx a časovač.
  • valueCtx: Podporuje přenos hodnot. Obsahuje základní kontext a klíč-hodnotu.

Při zrušení rodičovského kontextu jsou automaticky zrušeny všechny jeho potomci. To umožňuje šíření signálů zrušení nebo časových limitů řetězcem volání.

Příklad použití context.WithCancel:

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// Vytvoření kořenového kontextu a funkce zrušení
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Zajišťuje zrušení při ukončení main

	go func() {
		select {
		case <-ctx.Done(): // Čeká na zrušení kontextu
			fmt.Println("Úloha zrušena:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // Nebo skončí za 5 sekund
			fmt.Println("Úloha dokončena")
		}
	}()

	// Zrušení kontextu z jiného místa
	time.Sleep(1 * time.Second)
	fmt.Println("Zrušení kontextu...")
	cancel()

	// Dáváme čas gorutině zpracovat zrušení
	time.Sleep(100 * time.Millisecond)
}

Příklad použití 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("Zpracování požadavku pro User ID:", userID)
	} else {
		fmt.Println("User ID nebylo nalezeno v kontextu")
	}
}

func main() {
	ctx := context.WithValue(context.Background(), userIDKey("UserID"), "12345")
	processRequest(ctx)
}

Kontexty jsou bezpečné pro vlákna a neměnné, což je důležité pro konkurenční kód. Nejsou určeny k přenosu nestrukturovaných parametrů funkce, ale spíše k metainformacím ovlivňujícím provádění procesu.