Ответ
Для корректной обработки отмены контекста в горутине используется блокирующий select с проверкой канала ctx.Done(). Этот механизм позволяет горутине вовремя прекратить свою работу, освободить ресурсы и избежать утечек.
Основной паттерн:
Канал ctx.Done() закрывается, когда контекст отменяется (из-за вызова cancel(), истечения таймаута или дедлайна). Чтение из закрытого канала происходит немедленно.
func worker(ctx context.Context, dataChan <-chan string) error {
for {
select {
case <-ctx.Done():
// Контекст был отменен. Прекращаем работу.
fmt.Println("Worker: context cancelled.")
// Важно вернуть ошибку, чтобы вызывающий код знал причину остановки.
return ctx.Err()
case data, ok := <-dataChan:
if !ok {
// Канал с данными закрыт, работа завершена.
return nil
}
// Выполняем полезную работу с данными
fmt.Printf("Processing data: %sn", data)
time.Sleep(500 * time.Millisecond) // Имитация работы
}
}
}
Ключевые моменты и рекомендации:
-
Используйте
selectв циклах: В любой долгоживущей горутине, выполняющей работу в цикле,selectявляется обязательным для проверки отмены. -
Возвращайте
ctx.Err(): После получения сигнала изctx.Done(), проверьтеctx.Err(). Эта функция вернет причину отмены:context.Canceledилиcontext.DeadlineExceeded. -
Пробрасывайте контекст: Передавайте
context.Contextвниз по стеку вызовов всем функциям, которые могут быть долго выполняющимися или блокирующими (например, HTTP-запросы, запросы к БД). -
Освобождайте ресурсы: Перед выходом из функции по сигналу отмены убедитесь, что все захваченные ресурсы (файлы, соединения) корректно освобождены, например, с помощью
defer.
Ответ 18+ 🔞
А, слушай, вот этот ваш контекст, блядь, в Go — это ж просто песня, а не инструмент! Сидит себе горутина, работает, как проклятая, а ты ей сверху — раз! — и контекст отменил. И она должна, сука, моментально всё бросить, как будто её застукали за просмотром похабщины.
Вот смотри, как это правильно делается, чтобы не было потом утечек памяти, которые, блядь, как тараканы, выползут и съедят всю твою оперативку.
Главный фокус, на который нужно молиться:
Канал ctx.Done() — это твой спасательный круг. Как только контекст приказал долго жить (отменили, таймаут сработал, дедлайн наступил), этот канал закрывается. А читать из закрытого канала — это как получить пинка под зад: происходит мгновенно и без задержек.
func worker(ctx context.Context, dataChan <-chan string) error {
for {
select {
case <-ctx.Done():
// Контекст был отменен. Прекращаем работу.
fmt.Println("Worker: context cancelled.")
// Важно вернуть ошибку, чтобы вызывающий код знал причину остановки.
return ctx.Err()
case data, ok := <-dataChan:
if !ok {
// Канал с данными закрыт, работа завершена.
return nil
}
// Выполняем полезную работу с данными
fmt.Printf("Processing data: %sn", data)
time.Sleep(500 * time.Millisecond) // Имитация работы
}
}
}
А теперь, блядь, запомни раз и навсегда, чтобы не было потом мучительно больно:
selectв цикле — твой бронежилет. Любая горутина, которая крутится в вечном цикле и не проверяетctx.Done(), — это самоубийца. Она будет висеть, как довесок, пока всё приложение не накроется медным тазом.ctx.Err()— это отмазка. Верни её наверх, пусть знают, почему ты сдриснул: то ли тебя отменили ручками (context.Canceled), то ли ты проебал все сроки (context.DeadlineExceeded).- Тащи контекст везде, как самогон в деревне. Кидай его во все глубокие и тёмные места: в HTTP-клиенты, в запросы к базе, в долгие вычисления. Чтобы в любой момент можно было крикнуть "ВСЁ, ПИЗДЕЦ!" и всё остановилось.
- Прибери за собой, ёпта! Перед тем как выйти по сигналу отмены, закрой файлы, разорви соединения, освободи память. Используй
defer, это твой лучший друг, чтобы не оказаться в ситуации "запустил — забыл — охуел от утечек". Чистота — залог здоровья... приложения.