Ответ
Начиная с 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 стандартного способа передать причину не было. Разработчикам приходилось создавать собственные обертки над контекстом или использовать сторонние каналы для передачи информации об ошибке.