Как горутина определяет, что контекст отменён или истёк?

Ответ

Горутина определяет, что контекст отменён или истёк, используя механизм context.Context.

Основной способ проверки — это использование метода ctx.Done(), который возвращает канал. Когда контекст отменяется (например, вызовом cancel() для context.WithCancel или истечением времени для context.WithTimeout/context.WithDeadline), этот канал Done() закрывается. Закрытие канала является сигналом для всех горутин, ожидающих на нём.

Горутина может отслеживать этот сигнал с помощью оператора select:

func doWork(ctx context.Context) error {
    select {
    case <-ctx.Done():
        // Контекст отменён или истёк. Возвращаем ошибку контекста.
        fmt.Println("Работа отменена:", ctx.Err())
        return ctx.Err()
    default:
        // Продолжаем выполнять полезную работу, если контекст активен.
        fmt.Println("Работа продолжается...")
        // Например, можно выполнить какую-то операцию, которая также принимает контекст
        // time.Sleep(100 * time.Millisecond)
    }
    return nil
}

// Пример использования:
// ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// defer cancel()
// doWork(ctx)

Дополнительные способы проверки:

  • ctx.Err(): Этот метод возвращает ошибку, если контекст был отменён (context.Canceled) или истёк (context.DeadlineExceeded). Если контекст ещё активен, он возвращает nil. Обычно ctx.Err() проверяют после того, как ctx.Done() канал закрылся, чтобы узнать причину отмены.

Важные аспекты:

  • Иерархия контекстов: При отмене родительского контекста все его дочерние контексты (созданные с помощью context.WithCancel, context.WithTimeout, context.WithDeadline на основе родительского) также автоматически отменяются.
  • Пропагация отмены: Механизм context.Context позволяет легко распространять сигналы отмены по цепочке вызовов функций и горутин, обеспечивая корректное завершение операций и освобождение ресурсов.