Перейти до основного вмісту
Модуль 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() і не забудьте викликати скасування з відкладенням.

Завантаження редактора…
Показати підказку

`context.WithTimeout(parent, d)` повертає `(ctx, cancel)`; завжди скасувати виклик.

Рішення доступне після 3 спроб

вправи#go.m7.l5.e2
Спроби: 0Завантаження…

Використовуйте <-ctx.Done() у select, щоб дочекатися скасування контексту.

Завантаження редактора…
Показати підказку

`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() скасовується після відключення клієнта.