Ответ
Дочерний контекст создается из родительского для управления жизненным циклом и поведением конкретной операции или группы горутин. Это одна из ключевых идиом для написания надежного конкурентного кода в Go.
Основные цели:
-
Пропагация отмены (Cancellation): Если родительский контекст отменяется (например, из-за таймаута или явного вызова
cancel()), все дочерние контексты также автоматически отменяются. Это позволяет грациозно завершать цепочки зависимых операций и избегать утечек горутин. -
Установка дедлайнов и таймаутов (Deadlines and Timeouts): Можно создать дочерний контекст с ограничением по времени (
context.WithTimeoutилиcontext.WithDeadline). Это полезно для операций, которые не должны выполняться вечно, например, для сетевых запросов или обращений к базе данных. -
Передача данных внутри запроса (Request-scoped Values): С помощью
context.WithValueможно передавать данные вниз по стеку вызовов (например, ID запроса, токен аутентификации). Важно помнить, что это следует использовать только для данных, относящихся ко всему запросу, а не для передачи опциональных параметров в функцию.
Пример с таймаутом для HTTP-запроса:
// Создаем родительский контекст с таймаутом 2 секунды
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// defer cancel() гарантирует, что все ресурсы, связанные с контекстом, будут освобождены
defer cancel()
// Создаем и выполняем запрос с этим контекстом
req, _ := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)
resp, err := http.DefaultClient.Do(req)
// Если время вышло, err будет содержать context.DeadlineExceeded
if err != nil {
log.Println("Запрос не удался:", err)
}
Использование дочерних контекстов — это идиоматичный способ в Go для написания надежного, предсказуемого и отказоустойчивого конкурентного кода.
Ответ 18+ 🔞
Да ты посмотри, какой, блядь, механизм-то придумали! Контексты, сука! Ну не гениально ли, ёпта?
Вот представь: есть у тебя операция, которая может затянуться, как говно в проруби. Сетевой запрос, блядь, или в базу данных полез. А ты сидишь и ждёшь, пока он там, этот пидорас, решит ответить. Так вот, чтобы не ждать до второго пришествия, есть дочерние контексты.
Основная их фишка, блядь, в трёх вещах:
- Пропагация отмены. Это когда ты родительский контекст отменил — и все его дети, внуки и правнуки автоматом получают пизды. Всё, приехали, занавес. Никаких утечек горутин, которые будут висеть и жрать память, как голодный студент в столовой.
- Дедлайны и таймауты. Ну тут всё ясно, как божий день. Не хочешь ждать вечность — создаёшь контекст с таймаутом. «У тебя две секунды, ублюдок, иначе я тебя сам отменю!». Идеально для всего, что может зависнуть.
- Передача данных. Только, блядь, не вздумай тащить туда всё подряд, как в бабушкин сундук! Только то, что относится ко всему запросу: IDшник, токен аутентификации. Не делай из контекста глобальную помойку для параметров, а то получишь пизды от коллег.
Вот смотри, как это в коде выглядит, когда мы нетерпеливые:
// Родим контекст и говорим ему: «Старик, у тебя всего 2 секунды жизни. Не успел — извини».
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// А это магическая строчка, которая гарантирует, что даже если всё пойдёт по пизде, ресурсы почистятся.
defer cancel()
// Суём этот контекст в HTTP-запрос, как предсмертную записку.
req, _ := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)
resp, err := http.DefaultClient.Do(req)
// Если время вышло, то err будет равен context.DeadlineExceeded.
// Типа, «извини, братан, таймер пробил, пошёл ты нахуй».
if err != nil {
log.Println("Запрос не удался:", err)
}
Короче, если хочешь писать на Go код, который не сдохнет от первой же задержки и не будет висеть мёртвым грузом — учись юзать дочерние контексты. Это, блядь, как ремень безопасности: ебнешься — хоть не размажешь мозги по асфальту.