Как корректно обработать отмену 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.