Вопрос проверяет понимание модели конкурентности в Go и рисков, связанных с неограниченным созданием горутин, включая утечки памяти и исчерпание ресурсов.
Горутины в Go — это легковесные потоки, которые управляются рантаймом. Однако их создание без ограничений может привести к серьезным проблемам. Каждая горутина требует выделения стека (начиная с 2 КБ), который может расти до 1 ГБ при необходимости. Если горутины создаются в цикле или рекурсивно, они быстро исчерпывают доступную память.
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 1000000; i++ {
go func() {
time.Sleep(time.Hour) // горутина висит час
}()
}
fmt.Println("Горутины созданы")
time.Sleep(time.Minute)
}Этот код создаст миллион горутин, каждая из которых будет ждать час. Память быстро закончится, и программа упадет с ошибкой OOM (Out of Memory).
context.WithCancel или context.WithTimeout позволяют завершить горутину принудительно.errgroup.package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
select {
case <-time.After(time.Second):
fmt.Printf("Worker %d done\n", id)
case <-ctx.Done():
fmt.Printf("Worker %d cancelled\n", id)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
for i := 0; i < 10; i++ {
go worker(ctx, i)
}
time.Sleep(3 * time.Second)
}Здесь все горутины завершатся через 2 секунды благодаря контексту, что предотвращает утечку.
Вывод: Бесконтрольное создание горутин опасно из-за риска утечек памяти и падения производительности. Всегда используйте механизмы управления жизненным циклом (контексты, пулы, семафоры) для безопасной работы с конкурентностью в Go.