Как в Go ограничить время выполнения операции с помощью контекста?.

Ответ

Для ограничения времени выполнения операции в Go используется функция context.WithTimeout. Она создает дочерний контекст, который автоматически отменяется по истечении заданного времени.

context.WithTimeout является удобной оберткой над context.WithDeadline. Вызов context.WithTimeout(parent, d) эквивалентен context.WithDeadline(parent, time.Now().Add(d)).

Пример использования:

package main

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

// someLongOperation имитирует долгую операцию.
func someLongOperation(ctx context.Context) (string, error) {
    select {
    case <-time.After(3 * time.Second): // Имитация работы
        return "Result", nil
    case <-ctx.Done(): // Контекст был отменен
        return "", ctx.Err() // Возвращаем ошибку контекста
    }
}

func main() {
    // Создаем родительский контекст
    parentCtx := context.Background()
    // Создаем дочерний контекст с таймаутом в 2 секунды
    ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
    defer cancel() // Крайне важно вызывать cancel для освобождения ресурсов

    result, err := someLongOperation(ctx)
    if err != nil {
        // Проверяем, была ли ошибка вызвана именно таймаутом
        if errors.Is(err, context.DeadlineExceeded) {
            fmt.Println("Operation timed out!")
        } else {
            fmt.Println("Other error:", err)
        }
    } else {
        fmt.Println("Received result:", result)
    }
}

Ключевые аспекты и лучшие практики:

  1. Обязательный cancel(): Всегда вызывайте cancel() с помощью defer. Это освобождает ресурсы, связанные с контекстом, и предотвращает утечки, особенно если операция завершилась до истечения таймаута.
  2. Проверка ошибки: Операция, принимающая контекст, должна отслеживать его отмену (через select с case <-ctx.Done():). После вызова такой операции необходимо проверять ошибку ctx.Err(), которая будет равна context.DeadlineExceeded в случае таймаута.
  3. Каскадная отмена: Отмена родительского контекста (или его таймаут) приведет к немедленной отмене всех дочерних контекстов, созданных на его основе.