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