---
title: Context в Go
questionDates:
  q-transfer-0466: '2026-10-01'
  q-transfer-0467: '2026-10-01'
  q-transfer-0468: '2026-10-01'
  q-transfer-0469: '2026-10-01'
  q-transfer-0484: '2026-10-01'
  q-transfer-0485: '2026-10-01'
seo:
  description: >-
    Тема «Context в Go» для собеседования Go Developer. Как правильно ждать
    отмены через Context.Done? Как распространять context через цепочку вызовов?
  title: Context в Go — Go Developer
---

[Все темы Go Developer](/prep/go-developer)

## Как правильно ждать отмены через Context.Done? [#q-transfer-0466]

Долгая операция включает `ctx.Done()` в `select` рядом с работой или ожиданием. При закрытии канала она прекращает запуск новых действий, освобождает свои ресурсы и возвращает `ctx.Err()` либо ошибку с сохранённой причиной. Проверку выполняют в местах, где функция способна остановиться; один тест перед началом не отменит уже блокирующий вызов. Downstream API получает тот же context, чтобы отмена дошла до сети и БД. `Done` может быть `nil` для context без отмены, и `select` корректно игнорирует такой case. Функция не должна сама ждать, пока caller прочитает канал.

:::note[Ссылки для изучения]

1. [Done и select: остановка работы при отмене контекста — русская документация Go](https://spec-zone.ru/go/context/index)
   :::

---

## Как распространять context через цепочку вызовов? [#q-transfer-0467]

`context.Context` передают первым параметром, обычно с именем `ctx`, через всю цепочку request-scoped вызовов. Функция создаёт дочерний context только для более узкого deadline, отмены или значения и обязательно вызывает полученный `CancelFunc`. HTTP, SQL и RPC операции получают этот context своими context-aware методами. Его не хранят в struct и не заменяют `nil`; если контекст пока не нужен, передают `context.Background()` на верхней границе. Отмена родителя распространяется детям, но ребёнок не отменяет родителя. Контекст живёт не дольше запроса и не используется как контейнер всех параметров.

:::note[Ссылки для изучения]

1. [Передача context через цепочку вызовов и правила API — русская документация Go](https://spec-zone.ru/go/context/index)
2. [Context: каскадная отмена. Олег Козырев, с 1:59](https://www.youtube.com/watch?v=2cxmJUJ2Ge0&t=119s)

:::

---

## Что допустимо хранить в context.WithValue? [#q-transfer-0468]

В `context.WithValue` хранят небольшие неизменяемые request-scoped данные, которые передаются по цепочке вызовов: trace ID, аутентифицированного субъекта или метаданные запроса. Ключ задают собственным неэкспортируемым типом, чтобы пакеты не сталкивались по строке. Значение должно быть дополнительным, а не обязательной зависимостью функции. Конфигурацию, logger, БД, feature options и параметры бизнес-метода передают явно. Нельзя складывать изменяемый объект и ожидать потокобезопасности: context безопасен для конкурентного чтения, но не меняет гарантии самого значения.

:::note[Ссылки для изучения]

1. [context.WithValue: данные запроса и собственные типы ключей — русская документация Go](https://spec-zone.ru/go/context/index)
   :::

---

## Почему нужно вызывать CancelFunc после context.WithTimeout? [#q-transfer-0469]

`WithTimeout` создаёт таймер и дочерний context. Вызов `cancel` раньше дедлайна освобождает связанные ресурсы и сразу сообщает дочерним операциям, что результат больше не нужен. Обычный шаблон: сразу после успешного создания написать `defer cancel()`. Даже если функция завершилась быстро или вернула ошибку, cleanup выполнится. Автоматическое наступление deadline не заменяет явный вызов в нормальном раннем пути. `CancelFunc` можно вызывать повторно, но он не ждёт завершения goroutines: они должны слушать `Done` и самостоятельно закончить работу.

:::note[Ссылки для изучения]

1. [context.WithTimeout: таймеры, defer cancel и освобождение ресурсов — русская документация Go](https://spec-zone.ru/go/context/index)
2. [Context: вызов CancelFunc. Олег Козырев, с 11:35](https://www.youtube.com/watch?v=2cxmJUJ2Ge0&t=695s)

:::

---

## Безопасно ли вызывать cancel несколько раз? [#q-transfer-0484]

Да. `CancelFunc` можно вызывать несколько раз, в том числе конкурентно; последующие вызовы не меняют состояние. Канал `Done` закрывается один раз, а `Err` после отмены стабильно сообщает причину верхнего уровня. Это позволяет нескольким путям завершения использовать один cancel без отдельной защиты `sync.Once`. Но cancel не закрывает произвольные пользовательские каналы и не ждёт завершения goroutines. Если нужен join, его делают через `errgroup`, `WaitGroup` или другой протокол, а workers обязаны реагировать на `Done`.

:::note[Ссылки для изучения]

1. [CancelFunc: повторные и конкурентные вызовы, отсутствие ожидания — русская документация Go](https://spec-zone.ru/go/context/index)
   :::

---

## Как передать причину отмены контекста? [#q-transfer-0485]

`context.WithCancelCause` возвращает `CancelCauseFunc`, которому передают содержательную ошибку. `context.Cause(ctx)` возвращает её для отменённого context и потомков, тогда как `ctx.Err()` остаётся одной из общих ошибок `context.Canceled` или `context.DeadlineExceeded`. Это позволяет отличить «клиент ушёл» от «остановились после ошибки worker», не меняя стандартную проверку отмены. Первая установленная причина сохраняется по правилам распространения родителя и ребёнка; последующий cancel её не перезаписывает. Причина не должна содержать секреты и не заменяет wrapping ошибки на границе операции.

:::note[Ссылки для изучения]

1. [WithCancelCause и Cause: передача и наследование причины отмены — русская документация Go](https://spec-zone.ru/go/context/index)
   :::
