ПрограммированиеBackend разработчик

Что такое goroutine leak в Go и как их предотвращать?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса:

Goroutine leak — это ситуация, когда goroutine продолжает существовать и висит в памяти, хотя фактически "лишилась смысла" (вычисления завершились, данные не нужны, но нет условия выхода). Подобно утечкам памяти, но для потоков исполнения. Это критично для Go — большая нагрузка может привести к исчерпанию ресурсов.

Проблема:

В отличие от других языков, где прямое управление потоками часто ведёт к их ручному закрытию, в Go горутины запускаются легко, но не всегда корректно завершаются. Частая ошибка: основная логика завершилась, а горутина "замерзла" — ждёт данных по закрытому каналу или вообще не дождётся сигнала.

Решение:

Для предотвращения утечек используют контролирующие конструкции: context, закрытие каналов, сигнальные переменные. Важно заранее проектировать пути выхода из каждой горутины, использовать defer для очистки. Пример:

func worker(ctx context.Context, jobs <-chan int, results chan<- int) { for { select { case <-ctx.Done(): return case job, ok := <-jobs: if !ok { return } results <- job * 2 } } }

Ключевые особенности:

  • Контролируйте весь жизненный цикл goroutine
  • Используйте context для управляемого завершения
  • Закрывайте каналы после использования

Вопросы с подвохом.

Можно ли просто закрыть канал, чтобы остановить Goroutine?

Не всегда. Если в select есть другие case или нет проверки закрытия через ok, горутина может остаться "висеть".

val, ok := <-ch if !ok { return } // Так корректнее

Что произойдёт, если забыть обработать context.Done в select?

Горутина никогда не узнает, что отмена произошла — она останется "вечной". Это прямой путь к leak.

Можно ли поймать leak с помощью go runtime?

Нет стандартного инструмента отслеживания leak. Необходимо мониторить количество активных goroutine через runtime.NumGoroutine или использовать leak detector сторонних библиотек.

Типовые ошибки и анти-паттерны

  • Ожидание по несуществующему или заблокированному каналу
  • Запуск бесконечных горутин без путей выхода
  • Несогласованность в закрытии каналов

Пример из жизни

Негативный кейс

В системе рассылки пушей запускают goroutine на каждое входящее сообщение, но забывают останавливать их при отмене контекста или закрытии канала — сотни "мертвых" горутин висят в памяти.

Плюсы:

  • Просто запустить, быстрое прототипирование

Минусы:

  • Рост памяти
  • Замедление планировщика

Позитивный кейс

Работа goroutine контролируется через контекст, на уровне бизнес-логики проверяется выход из select, после успешной отправки/обработки канал завершается.

Плюсы:

  • Нет утечек
  • Легко следить и профилировать

Минусы:

  • Нужно аккуратное проектирование каналов и потоков