Как са структурирани контекстите в езика Go?
sobes.tech AI
Отговор от AI
Контекстът (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)
}
Контекстите са безопасни за нишки и неизменяеми, което е важно за конкурентния код. Те не са предназначени за пренасяне на необработени параметри, а по-скоро за метаинформация, която влияе върху изпълнението на процеса.