Перейти к основному содержимому
Модуль 7 · Урок 5 из 535/50 в курсе~14 min
Уроки модуля (5/5)

`context.Context`: отмена и сроки

Пакет context является стандартом де-факто для распространения отмена, сроки и ограниченные значения по цепочке вызовов, в нескольких горутинах. Все стандартные «сетевые» пакеты (net/http, database/sql, os/exec) примите context.Context как их первый параметр.

Интерфейс

Go
type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any
}
  • Done(): канал, который закрывается при отмене контекста.
  • Err(): причина отмены (context.Canceled или context.DeadlineExceeded).
  • Deadline(): необязательный крайний срок.
  • Value(): поиск значений, привязанных к области (используйте экономно).

Четыре основные функции

Go
ctx := context.Background()              // radice: usalo in main, init, test
ctx := context.TODO()                    // "non so ancora": stub esplicito

ctx, cancel := context.WithCancel(parent)               // cancel manuale
ctx, cancel := context.WithTimeout(parent, d)           // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t)          // scadenza assoluta

ctx := context.WithValue(parent, key, value)            // valore scope-bound

Все производные формы возвращают функцию cancel: всегда вызывайте ее. (в идеале с defer), чтобы освободить внутренние ресурсы, даже если контекст уже истек.

Шаблон тайм-аута

Go
func fetchUser(id string) (*User, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    return queryUser(ctx, id)   // queryUser deve rispettare ctx
}

Если queryUser занимает более 2 секунд, ctx.Done() закрывается и функция должна прервать операцию.

Соблюдая контекст: выберите Done().

Go
func slowWork(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()      // canceled o deadline exceeded
        case <-time.After(100 * time.Millisecond):
            // un tick di lavoro
        }
    }
}

Правило: везде, где ваша функция может заблокироваться на длительное время, она должен обрабатывать ctx.Done(). В противном случае контекст «включен» на ничего.

Соглашение о первом параметре

Go
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
//        ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionale
  • ctx ВСЕГДА в качестве первого параметра с именем ctx.
  • Никогда не помещайте Context в качестве поля структуры (очень редкие исключения).
  • Никогда не передавайте nil: используйте context.TODO() в качестве заглушки.

context.WithValue: экономно

Go
type userKey struct{}

ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }

Рекомендации:

  • ТОЛЬКО для данных области запроса (идентификатор запроса, идентификатор трассировки, идентификатор пользователя).
  • НИКОГДА для параметров функции: передавайте их как обычно.
  • Ключи типа пользовательский неэкспортируемый, чтобы избежать коллизий.

Распространение по цепочке

Go
func handleHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()   // context legato al ciclo di vita della richiesta
    user, err := fetchUser(ctx, r.URL.Query().Get("id"))
    if err != nil { ... }
    // ...
}

net/http автоматически отменяет r.Context(), когда клиент отключается или когда обработчик возвращается. Распространяя это на всех нисходящие вызовы, все дерево операций отменяется в каскад: нет напрасной работы после отключения.

Попробуйте это

Упражнение#go.m7.l5.e1
Попыток: 0Загрузка…

Создайте контекст с таймаутом в 1 секунду, полученным из context.Background(), и не забудьте вызвать команду cancel с помощью defer.

Загрузка редактора…
Показать подсказку

`context.WithTimeout(parent, d)` возвращает `(ctx, cancel)`; всегда звоните, отменяйте.

Решение доступно после 3 попыток

Упражнение#go.m7.l5.e2
Попыток: 0Загрузка…

Используйте <-ctx.Done() при выборе, чтобы дождаться отмены контекста.

Загрузка редактора…
Показать подсказку

`ctx.Done()` — это канал, который закрывается при отмене контекста.

Решение доступно после 3 попыток

Quiz#go.m7.l5.e3
Готово

По соглашению, где находится параметр ctx в сигнатуре функции?

Go
func Fetch(???, url string) ([]byte, error)
Варианты ответа

Резюме

  • context.Context распространяет значения отмены, крайнего срока и области действия.
  • Background()/TODO() в качестве корня; WithCancel/WithTimeout/WithDeadline для получения.
  • Всегда defer cancel() для освобождения ресурсов.
  • «Длинные» функции должны select в ctx.Done() или передавать ctx в стандартные пакеты. – Соглашение: ctx в качестве ПЕРВОГО параметра, никогда внутри структуры.
  • WithValue только для данных области запроса (идентификатор запроса, идентификатор трассировки).
  • В net/http r.Context() отменяется при отключении клиента.