Sobes.tech
Middle

Kako su strukturirani konteksti u jeziku Go?

sobes.tech АИ

Одговор од АИ

Контекст (context.Context) у Go се користи за пренос информација о роковима, отказима и другим подацима између граница API-ја. Он представља дрво: сваки контекст је потомак другог, осим коренског (context.Background() или context.TODO()).

Главне методе интерфејса context.Context:

  • Deadline() (deadline time.Time, ok bool): Враћа рок контекста.
  • Done() <-chan struct{}: Враћа канал који се затвара приликом отказа контекста.
  • Err() error: Враћа грешку која је довела до отказа контекста.
  • Value(key any) any: Враћа вредност повезану са кључем у контексту.

Креирају се помоћу функција пакета context:

  • context.Background(): Коренски празан контекст, користи се као основа за главни процес.
  • context.TODO(): Користи се као привремена замена када није познато који контекст користити.
  • context.WithCancel(parent Context): Креира дечији контекст и функцију за отказивање.
  • context.WithDeadline(parent Context, d time.Time): Креира дечији контекст са роком.
  • context.WithTimeout(parent Context, timeout time.Duration): Креира дечији контекст са временским ограничењем.
  • context.WithValue(parent Context, key, val any): Креира дечији контекст са вредношћу.

Главне имплементације Context:

  • emptyCtx: Основни, неотказиви и без вредности. Користе се Background и TODO.
  • cancelCtx: Подржава отказивање. Састоји се од основног контекста и канала за отказивање.
  • timerCtx: Подржава рок/таймер. Садржи cancelCtx и тајмер.
  • valueCtx: Подржава пренос вредности. Садржи основни контекст и кључ-вредност.

При отказу родитељског контекста, автоматски се отказују сви дечији. Ово омогућава ширење сигнала отказа или рокова кроз ланац позива.

Пример коришћења context.WithCancel:

package main

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

func main() {
	// Креирање коренског контекста и функције за отказивање
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // Гарантује отказивање при изласку из main

	go func() {
		select {
		case <-ctx.Done(): // Чекамо на отказ контекста
			fmt.Println("Задача отказана:", ctx.Err())
			return
		case <-time.After(5 * time.Second): // Или завршава за 5 секунди
			fmt.Println("Задача извршена")
		}
	}()

	// Отказивање контекста из другог места
	time.Sleep(1 * time.Second)
	fmt.Println("Отказивање контекста...")
	cancel()

	// Дамо време горутини да обради отказ
	time.Sleep(100 * time.Millisecond)
}

Пример коришћења 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("Обрада захтева за User ID:", userID)
	} else {
		fmt.Println("User ID није пронађен у контексту")
	}
}

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

Контексти су безбедни за рад са нитима и неизменљиви, што је важно за конкурентни код. Они нису намењени за пренос необрађених параметара функција, већ за мета-информације које утичу на извршавање процеса.