Ответ
Начиная с Go 1.20, появилась стандартная возможность узнать причину отмены контекста. Для этого используется функция context.Cause(ctx)
.
Основная идея:
- При создании контекста, который может быть отменен с указанием причины, используется
context.WithCancelCause(parent)
. - Эта функция возвращает
cancel
функцию типаfunc(cause error)
. При вызове этой функции вы передаете ошибку, которая и является причиной отмены. - В горутине, которая работает с этим контекстом, после получения сигнала
<-ctx.Done()
, можно вызватьcontext.Cause(ctx)
для получения этой ошибки.
Пример:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Создаем контекст с возможностью указания причины отмены
ctx, cancel := context.WithCancelCause(context.Background())
// Запускаем горутину, которая будет отменена
go func(ctx context.Context) {
select {
case <-ctx.Done():
// Контекст отменен, узнаем причину
err := context.Cause(ctx)
fmt.Printf("Работа прервана, причина: %vn", err)
}
}(ctx)
time.Sleep(100 * time.Millisecond)
// Отменяем контекст, передавая конкретную ошибку
cancel(fmt.Errorf("операция больше не нужна"))
time.Sleep(100 * time.Millisecond) // Даем время на вывод
}
// Вывод: Работа прервана, причина: операция больше не нужна
Возможные значения context.Cause(ctx)
:
nil
, если контекст еще не отменен (ctx.Err() == nil
).context.Canceled
, если контекст был отменен черезcancel()
без указания причины (например, изcontext.WithCancel()
) илиcancel(nil)
.context.DeadlineExceeded
, если контекст был отменен по таймауту (WithTimeout
) или дедлайну (WithDeadline
).- Пользовательская ошибка (
error
), если контекст был отменен вызовомcancel(err)
с не-nil ошибкой.
До Go 1.20 стандартного способа передать причину не было. Разработчикам приходилось создавать собственные обертки над контекстом или использовать сторонние каналы для передачи информации об ошибке.