Как корректно обработать отмену context.Context в горутине?

Ответ

Для корректной обработки отмены контекста в горутине используется блокирующий 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) // Имитация работы
        }
    }
}

Ключевые моменты и рекомендации:

  1. Используйте select в циклах: В любой долгоживущей горутине, выполняющей работу в цикле, select является обязательным для проверки отмены.

  2. Возвращайте ctx.Err(): После получения сигнала из ctx.Done(), проверьте ctx.Err(). Эта функция вернет причину отмены: context.Canceled или context.DeadlineExceeded.

  3. Пробрасывайте контекст: Передавайте context.Context вниз по стеку вызовов всем функциям, которые могут быть долго выполняющимися или блокирующими (например, HTTP-запросы, запросы к БД).

  4. Освобождайте ресурсы: Перед выходом из функции по сигналу отмены убедитесь, что все захваченные ресурсы (файлы, соединения) корректно освобождены, например, с помощью 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) // Имитация работы
        }
    }
}

А теперь, блядь, запомни раз и навсегда, чтобы не было потом мучительно больно:

  1. select в цикле — твой бронежилет. Любая горутина, которая крутится в вечном цикле и не проверяет ctx.Done(), — это самоубийца. Она будет висеть, как довесок, пока всё приложение не накроется медным тазом.
  2. ctx.Err() — это отмазка. Верни её наверх, пусть знают, почему ты сдриснул: то ли тебя отменили ручками (context.Canceled), то ли ты проебал все сроки (context.DeadlineExceeded).
  3. Тащи контекст везде, как самогон в деревне. Кидай его во все глубокие и тёмные места: в HTTP-клиенты, в запросы к базе, в долгие вычисления. Чтобы в любой момент можно было крикнуть "ВСЁ, ПИЗДЕЦ!" и всё остановилось.
  4. Прибери за собой, ёпта! Перед тем как выйти по сигналу отмены, закрой файлы, разорви соединения, освободи память. Используй defer, это твой лучший друг, чтобы не оказаться в ситуации "запустил — забыл — охуел от утечек". Чистота — залог здоровья... приложения.