Ответ
context.Context
в Go — это стандартный механизм для передачи дедлайнов, сигналов отмены и других данных (request-scoped values) между горутинами. Он является неотъемлемой частью написания надежного и управляемого конкурентного кода, особенно в сетевых сервисах и распределенных системах.
Основные сценарии использования:
Отмена (Cancellation): Если пользователь отменил запрос (например, закрыл вкладку браузера),
context
позволяет каскадно передать сигнал отмены всем горутинам, которые обрабатывали этот запрос (например, запросы к БД, другим сервисам), чтобы они прекратили работу и освободили ресурсы.- Создается с помощью
context.WithCancel()
. - Проверяется через канал
<-ctx.Done()
.
- Создается с помощью
Таймауты и Дедлайны (Timeouts and Deadlines): Можно установить максимальное время на выполнение операции. Если время истекло, контекст будет отменен. Это критически важно для предотвращения "зависания" горутин в ожидании ответа от медленного внешнего сервиса.
- Создается с помощью
context.WithTimeout()
илиcontext.WithDeadline()
.
- Создается с помощью
Передача данных (Value Propagation): Позволяет передавать данные вниз по стеку вызовов, например, ID запроса, информацию для трассировки или данные о пользователе.
- Создается с помощью
context.WithValue()
. - Важно: Считается плохой практикой использовать
WithValue
для передачи обязательных параметров функции. Параметры должны передаваться явно. Контекст предназначен только для сквозных, request-scoped данных.
- Создается с помощью
Пример с таймаутом:
package main
import (
"context"
"fmt"
"time"
)
func longOperation(ctx context.Context) {
select {
case <-time.After(3 * time.Second):
fmt.Println("Операция успешно завершена")
case <-ctx.Done(): // Этот канал закроется при отмене контекста
fmt.Println("Операция прервана по таймауту:", ctx.Err())
}
}
func main() {
// Создаем контекст с таймаутом в 2 секунды
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // Важно вызывать cancel, чтобы освободить ресурсы
longOperation(ctx) // Операция будет прервана через 2 секунды
}
Правила использования Context
:
- Передавайте
Context
как первый аргумент функции, с именемctx
. - Никогда не храните
Context
внутри структуры. Передавайте его явно в каждый вызов. - Цепочка вызовов должна пробрасывать контекст дальше.
- Никогда не передавайте
nil
Context
. Если не уверены, какой использовать, используйтеcontext.Background()
.