Обнаружение и устранение утечек горутин в Go‑приложении

После тайм-аута получатель уходит, а горутина навсегда блокируется на отправке в небуферизированный канал. Минимальное исправление — буфер на один результат; полноценное — отменяемая медленная операция.

Короткий ответ

Что ответить на собеседовании

После тайм-аута получатель уходит, а горутина навсегда блокируется на отправке в небуферизированный канал. Минимальное исправление — буфер на один результат; полноценное — отменяемая медленная операция.

Подробный разбор

Ответ с пояснениями

Условие

В данном фрагменте кода присутствует потенциальная проблема, связанная с управлением горутин и возможными утечками ресурсов. Требуется проанализировать программу, выявить её слабые места и предложить корректный способ решения.

package main

import (
  "fmt"
  "runtime"
  "time"
)

func main() {
  for i := 0; i < 100; i++ {
    requestData(1)
  }

  time.Sleep(time.Second * 1)
  fmt.Printf("Number of hanging goroutines: %d\n", runtime.NumGoroutine())
}

func requestData(timeout time.Duration) string {
  dataChan := make(chan string)

  go func() {
    dataChan <- requestFromSlowServer()
  }()

  select {
  case result := <-dataChan:
    fmt.Printf("[+] request returned: %s\n", result)
    return result
  case <-time.After(timeout):
    fmt.Println("[!!] request timeout!")
    return ""
  }
}

func requestFromSlowServer() string {
  time.Sleep(time.Second * 1)
  return "very important data"
}

Задача состоит в том, чтобы понять, почему после завершения цикла остаются «зависшие» горутины, и предложить изменения кода, которые гарантируют корректное завершение всех запущенных процессов независимо от таймаутов.

Ответ

После тайм-аута получатель уходит, а горутина навсегда блокируется на отправке в небуферизированный канал. Минимальное исправление — буфер на один результат; полноценное — отменяемая медленная операция.

Замените make(chan string) на make(chan string, 1): для этой функции с одной отправкой работник сможет записать результат и завершиться даже без получателя. Не закрывайте канал со стороны тайм-аута. Учтите, requestData(1) означает одну наносекунду, не секунду; передавайте time.Second. Буфер не остановит сам зависший запрос: для гарантированного завершения нужен context в сетевой операции и ожидание работников через WaitGroup. NumGoroutine включает не только ваши горутины.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее