> Куда попадает функция при вызове go в Go? В какую очередь? (Go)
Уровень: middle · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: InDrive
Стек: Go
> Пример ответа
Короткий ответ
При вызове go func() функция попадает не в очередь, а в планировщик Go runtime. Она становится горутиной и помещается в локальную очередь (local run queue) текущего P (processor), либо в глобальную очередь (global run queue), если локальная переполнена. Дальше планировщик распределяет горутины по доступным потокам ОС (M) в рамках модели GMP.
Подробное объяснение
В Go используется модель планирования GMP:
- G - горутина (goroutine), единица работы.
- M - поток операционной системы (machine), который выполняет горутины.
- P - логический процессор (processor), контекст выполнения, который связывает G и M.
При вызове go f() runtime создаёт новую горутину G и пытается поместить её в локальную очередь текущего P. Локальная очередь - это кольцевой буфер фиксированного размера (обычно 256 элементов). Если локальная очередь заполнена, горутина отправляется в глобальную очередь - общий список, защищённый мьютексом.
Планировщик работает по принципу work stealing: когда у P заканчиваются горутины в локальной очереди, он пытается украсть половину горутин из локальной очереди другого P, и только потом обращается к глобальной очереди. Глобальная очередь также периодически проверяется каждым P (каждые 61 тик планировщика), чтобы избежать голодания горутин, попавших в неё.
Важно: это не классическая очередь задач в смысле FIFO. Планировщик может переставлять горутины, вытеснять их (preemption) и менять порядок выполнения. Горутина не ждёт своей очереди пассивно - она либо выполняется, либо заблокирована на канале, мьютексе или системном вызове.
На практике
Для backend-разработчика важно понимать, что:
- Количество одновременно выполняющихся горутин ограничено числом P (по умолчанию
GOMAXPROCS, обычно равно числу ядер CPU). - Остальные горутины ждут в очередях, но это ожидание не блокирует поток ОС - M может переключиться на другую горутину.
- При системных вызовах (например, чтение из сети) P отсоединяется от M, и M блокируется, а P переходит к другому M - это позволяет не простаивать ядрам.
- Не стоит создавать миллионы горутин бездумно: каждая занимает минимум 2 КБ стека, плюс накладные расходы на планирование.
Пример кода
GOpackage mainimport ("fmt""runtime""sync")func main() {runtime.GOMAXPROCS(2) // 2 P, значит до 2 горутин выполняются одновременноvar wg sync.WaitGroupfor i := 0; i < 10; i++ {wg.Add(1)go func(n int) {defer wg.Done()fmt.Println("goroutine", n)}(i)}wg.Wait()}
Здесь 10 горутин создаются и попадают в очереди планировщика. При двух P одновременно выполняются только две, остальные ждут в локальных или глобальной очередях.
Как отвечать на собеседовании
Начни с короткого ответа: "функция становится горутиной и попадает в планировщик, в локальную очередь P или глобальную". Затем раскрой модель GMP, упомяни work stealing и вытеснение. Если спросят про очередь - подчеркни, что это не простая FIFO, а сложная система с приоритетами и перераспределением. Хорошо добавить пример с GOMAXPROCS и объяснить, почему горутины не блокируют потоки ОС.
Что проверяет интервьюер
Интервьюер проверяет:
- Понимание модели GMP, а не поверхностное знание "горутины лёгкие".
- Знание разницы между локальной и глобальной очередями.
- Понимание work stealing и почему он нужен.
- Осознание, что планировщик - это не очередь задач, а сложный механизм с вытеснением и блокировками.
- Умение объяснить, как горутины взаимодействуют с системными вызовами.
Типичные ошибки
- Ответ "функция попадает в очередь задач" без упоминания планировщика и P.
- Утверждение, что горутины выполняются строго в порядке создания - это не так.
- Путаница между очередью горутин и очередью сообщений в каналах.
- Игнорирование глобальной очереди и work stealing.
- Утверждение, что количество горутин ограничено числом ядер - на самом деле ограничено только памятью, одновременно выполняющихся - числом P.
> Похожие задачи по Go
Как происходит эвакуация элементов в хэш-таблице?
Что происходит с паниками в Go?
Можно ли сделать длину канала для задач равной 10?
Как исправить SQL запрос, который собирает строку без контекста?
> Похожие задачи по backend
Как происходит эвакуация элементов в хэш-таблице?
Что происходит с паниками в Go?
Можно ли сделать длину канала для задач равной 10?
Как исправить SQL запрос, который собирает строку без контекста?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью