Context Cancellation - это не kill -9 Одна из самых частых иллюзий у разработчиков, приходящих в Go: "Я вызвал cancel(), почему моя горутина всё еще р…
Одна из самых частых иллюзий у разработчиков, приходящих в Go:
"Я вызвал cancel(), почему моя горутина всё еще работает?"
Важно запомнить: В Go нельзя принудительно убить горутину снаружи. Нет никакого PID, по которому можно стрельнуть сигналом.
context в Go реализует кооперативную отмену.
Это значит, что cancel() - это не приказ "Умри!", а вежливое сообщение: "Брат, ты нам больше не нужен, сворачивайся, как сможешь".
Если ваша горутина не проверяет этот канал, она будет молотить до победного конца (или до паники), даже если клиент давно отвалился по таймауту.
❌ Как делать НЕ надо (Игнорирование)
func HeavyWork(ctx context.Context) {
// Мы передали контекст, но... не используем его
for i := 0; i < 1000000; i++ {
calculateHash(i) // Жжёт CPU впустую, если ctx отменен
}
}
✅ Как надо (Проверка канала)
В любом долгом цикле или блокирующей операции вы обязаны слушать ctx.Done().
func HeavyWork(ctx context.Context) error {
for i := 0; i < 1000000; i++ {
// Вариант 1: Неблокирующая проверка в каждой итерации
select {
case <-ctx.Done():
return ctx.Err() // "context canceled" или "deadline exceeded"
default:
// Работаем дальше
}
calculateHash(i)
}
return nil
}
Нюанс для Сеньоров: context.Cause (Go 1.20+)
Раньше, получая ctx.Err(), мы видели просто context canceled. Это неинформативно. Кто отменил? Почему?
Начиная с Go 1.20, используйте WithCancelCause:
ctx, cancel := context.WithCancelCause(parent)
// Где-то в логике отмены:
cancel(fmt.Errorf("client disconnect inside handler"))
// В горутине:
if ctx.Err() != nil {
fmt.Println(context.Cause(ctx)) // Печатает конкретную причину!
}
☝️Context - это кровеносная система вашего приложения. Если вы пишете функцию, которая делает что-то дольше 10мс или ходит в сеть/базу - всегда принимайте первым аргументом ctx и всегда прокидывайте его дальше. Библиотеки (pgx, net/http) уже умеют его слушать, просто дайте им этот шанс.
Не плодите горутины-зомби. 🧟♂️
#golang #context #concurrency #bestpractices
👉 @golang_lib