> Почему в Go можно создать тысячи горутин (Go)
Уровень: middle · Роль: backend · Категория: Технические вопросы
Компании: ЦУМ, VK, Ozon, Avito
Стек: Go
> Пример ответа
Короткий ответ
В Go можно создавать тысячи горутин, потому что они - это не потоки операционной системы, а легковесные пользовательские единицы выполнения, управляемые рантаймом. Каждая горутина изначально занимает всего несколько килобайт стека (обычно 2-8 КБ), который динамически растёт и сжимается. Рантайм распределяет горутины по небольшому числу потоков ОС (обычно GOMAXPROCS) через собственный планировщик, что позволяет создавать миллионы горутин без значительных накладных расходов на память и переключение контекста.
Подробное объяснение
Горутина - это функция, которая выполняется конкурентно с другими горутинами в том же адресном пространстве процесса. Ключевое отличие от потоков ОС:
- Поток ОС имеет фиксированный стек (обычно 1-8 МБ), создаётся и уничтожается системным вызовом, переключение контекста требует перехода в режим ядра и стоит дорого (микросекунды).
- Горутина создаётся в пользовательском пространстве, её стек изначально крошечный (2-8 КБ) и растёт по мере необходимости (до 1 ГБ максимум). Переключение между горутинами - это просто сохранение и восстановление регистров и указателя стека, что занимает наносекунды.
Планировщик Go реализует модель M:N: M потоков ОС выполняют N горутин. Рантайм использует:
- Локальные очереди для горутин на каждом потоке.
- Глобальную очередь для горутин, ожидающих выполнения.
- Work stealing - потоки забирают горутины из очередей других потоков для балансировки нагрузки.
- Системные вызовы - если горутина блокируется на syscall, поток ОС может быть передан другой горутине, а заблокированная горутина переходит в специальное состояние.
Когда горутина блокируется на канале, мьютексе или таймере, она не занимает поток ОС - планировщик просто переключается на другую горутину. Это делает горутины идеальными для I/O-bound задач и высококонкурентных сервисов.
На практике
На практике это означает, что можно писать код, который создаёт десятки тысяч горутин без риска исчерпать память или упасть по лимиту процессов. Например, обработка каждого HTTP-запроса в отдельной горутине - стандартный паттерн в Go. При этом важно помнить:
- Горутины не бесплатны: каждая занимает память под стек и структуру
g(около 2-4 КБ служебных данных). - Нужно контролировать завершение горутин, чтобы избежать утечек (использовать
context.Context, каналы,sync.WaitGroup). - Для ограничения параллелизма (например, при работе с внешними API) используют семафоры или worker pool.
Пример кода
GOpackage mainimport ("fmt""sync")func main() {const n = 100000 // 100 тысяч горутинvar wg sync.WaitGroupwg.Add(n)for i := 0; i < n; i++ {go func(id int) {defer wg.Done()// имитация полезной работы_ = id * id}(i)}wg.Wait()fmt.Println("все горутины завершились")}
Этот код создаёт 100 000 горутин и успешно завершается, потребляя всего несколько десятков мегабайт памяти. Аналогичный код с потоками ОС упал бы на лимите процессов или исчерпал память.
Как отвечать на собеседовании
Начни с ключевого отличия: горутины - это не потоки ОС, а легковесные сущности рантайма. Упомяни размер стека (2-8 КБ, динамический рост) и стоимость переключения (наносекунды против микросекунд). Затем объясни модель планировщика: M:N, work stealing, локальные и глобальные очереди. Подчеркни, что блокировка на канале или мьютексе не занимает поток ОС. Если спросят про ограничения - скажи, что лимит упирается в память (примерно 2-4 КБ на горутину служебных данных) и что на практике миллионы горутин возможны, но обычно не нужны.
Что проверяет интервьюер
Интервьюер проверяет:
- Понимание разницы между горутинами и потоками ОС.
- Знание внутреннего устройства планировщика Go (M:N, work stealing).
- Понимание trade-off: горутины дешевле, но не бесплатны.
- Умение объяснить, почему горутины подходят для I/O-bound задач.
- Осознание практических ограничений (память, утечки, контроль завершения).
Типичные ошибки
- Утверждение, что горутины - это просто потоки ОС, только легче.
- Забывают упомянуть динамический размер стека.
- Говорят, что горутины не имеют накладных расходов вообще.
- Путают параллелизм (parallelism) и конкурентность (concurrency).
- Не упоминают, что блокировки на каналах не занимают поток ОС.
- Считают, что GOMAXPROCS - это максимальное количество горутин (на самом деле - количество потоков ОС для выполнения горутин).
> Похожие задачи по backend
Расскажите про опыт работы с базами данных и используемые СУБД
Что такое нормализация и денормализация баз данных
В чем различия между HTTP/1.1 и HTTP/2
Расскажите про опыт работы с Go
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью