Ответ
В Go проверка отмены контекста является ключевой частью написания корректного конкурентного кода. Существует несколько способов это сделать.
1. Блокирующая проверка через select
(основной способ)
Самый идиоматичный способ — использовать select
для ожидания сигнала из канала ctx.Done()
. Этот канал закрывается, когда контекст отменяется.
func operation(ctx context.Context) {
select {
case <-ctx.Done():
// Контекст был отменен, нужно завершить работу.
// ctx.Err() вернет причину отмены.
fmt.Println("Operation canceled:", ctx.Err()) // -> "context canceled" или "context deadline exceeded"
return
case <-time.After(2 * time.Second): // Имитация долгой работы
fmt.Println("Operation completed successfully")
}
}
func main() {
// Контекст, который отменится через 1 секунду
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() // Хорошая практика - вызывать cancel, даже если контекст с таймаутом
operation(ctx)
}
2. Неблокирующая проверка
Если вам нужно просто проверить статус контекста, не блокируя выполнение, можно использовать select
с default
веткой.
func isContextDone(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
3. Проверка ошибки ctx.Err()
Метод ctx.Err()
напрямую возвращает причину отмены.
- Он вернет
nil
, если контекст еще не отменен. - Он вернет
context.Canceled
, если была вызвана функцияcancel()
. - Он вернет
context.DeadlineExceeded
, если истек таймаут или дедлайн.
Этот метод полезен для проверки состояния контекста после того, как какая-то блокирующая операция завершилась с ошибкой.
// ... какая-то операция завершилась с ошибкой `err` ...
if err != nil {
// Проверяем, не была ли ошибка вызвана отменой контекста
if ctx.Err() == context.Canceled {
log.Println("Operation failed because context was canceled by caller")
} else if ctx.Err() == context.DeadlineExceeded {
log.Println("Operation failed because it hit the deadline")
}
}