Планировщик Go: все что нужно знать на собесе (ч.1)
Планировщик в Go спрашивают на каждом втором собеседовании на мидла+ и синьора. Я прошел десятки собесов включая Яндекс, Тинькофф, ВК, ВБ и могу сказать точно: кто не знает глубже GMP модели, тот фейлит техничку
Разберем что реально спрашивают
Начнем с базы: зачем вообще нужен планировщик?
Представь наивный подход:
Три буквы, которые надо знать наизусть: G, M, P
Скажешь это на собесе, интервьюер уже понимает что ты ахуй умный ♟
Готовая формулировка:
Где лежат горутины, готовые к выполнению?
Тут часто ловят на собесе: «а как обеспечивается справедливость в нагрузке?»
Spinning threads: почему рантайм не будит поток на каждую готовую горутину?
НО ВСЕ ЭТО БЫЛА ТОЛЬКО БАЗА
Самые жесткие вопросы на собесе начинаются дальше. Именно на них валятся 80% кандидатов, потому что читали статьи 2019 года и не знают, что изменилось. А интервьюеры это прекрасно видят
Системные вызовы: любимый вопрос для синьоров
«Что происходит, когда горутина делает блокирующий syscall?»
Сколько OS threads может создать Go программа?
Тут надо отвечать двумя слоями, иначе выглядишь как человек, который прочитал одну статью на хабре и пришел на собес
Слой 1: есть лимит, по умолчанию 10 000 потоков, настраивается через runtime/debug.SetMaxThreads
Слой 2: Go создает новый поток НЕ потому что «надо больше параллелизма», а когда все существующие потоки заблокированы в syscalls, cgo или залочены через LockOSThread
Формулировка для собеса:
На этом тормозим
Во второй части разберем вытеснение (кооперативное vs асинхронное), что изменилось в Go 1.14, GOMAXPROCS в 2026 и контейнерах, а также топ-вопросы с собесов с готовыми ответами. Там будет самый сок
Вторая часть выйдет через несколько дней
Планировщик в Go спрашивают на каждом втором собеседовании на мидла+ и синьора. Я прошел десятки собесов включая Яндекс, Тинькофф, ВК, ВБ и могу сказать точно: кто не знает глубже GMP модели, тот фейлит техничку
Разберем что реально спрашивают
Начнем с базы: зачем вообще нужен планировщик?
Представь наивный подход:
1 горутина = 1 поток ОС.
Работает? Да. Масштабируется? Нет, потому что потоки это дорого: жирный стек, переключение контекста, давление на планировщик OC
Поэтому в Go используется модель M:N, где множество горутин мультиплексируются на маленькое число потоков ОС. И именно для этого существует планировщик в рантайме
Три буквы, которые надо знать наизусть: G, M, P
→ G (goroutine) это единица работы, та самая легковесная горутина
→ M (machine) это реальный поток ОС, worker thread
→ P (processor) это контекст планирования, без которого поток M не может выполнять Go-код. По сути квота на исполнение
Скажешь это на собесе, интервьюер уже понимает что ты ахуй умный ♟
Готовая формулировка:
Go мультиплексирует множество G на меньшее число M, а P это квота и контекст исполнения Go-кода. Связка M+P дает возможность исполнять Go, а число P ограничивает параллелизм
Где лежат горутины, готовые к выполнению?
У каждого P есть локальная очередь (per-P runq), быстрая, без глобального лока. Плюс есть глобальная очередь для балансировки
Тут часто ловят на собесе: «а как обеспечивается справедливость в нагрузке?»
Большинство кандидатов в этот момент начинают мычать что-то невнятное. А ответ простой: P основную часть времени ест из локальной очереди (скорость + локальность), но раз в 61 тик проверяет глобальную, чтобы горутины там не голодали
Work stealing: когда один P пустой, а другой забит
Если у P нет работы в локальной очереди, он пытается украсть половину у другого P. Это основа балансировки: распределенное состояние + stealing вместо центрального лок-бутылочного горлышка
Spinning threads: почему рантайм не будит поток на каждую готовую горутину?
Потому что если будить на каждый чих, получишь thrashing park/unpark и все ляжет нахуй. Рантайм аккуратно управляет этим: будит новый поток только если есть idle P И нет spinning worker threads. Сначала ищут работу, и только потом паркуются
НО ВСЕ ЭТО БЫЛА ТОЛЬКО БАЗА
Самые жесткие вопросы на собесе начинаются дальше. Именно на них валятся 80% кандидатов, потому что читали статьи 2019 года и не знают, что изменилось. А интервьюеры это прекрасно видят
Системные вызовы: любимый вопрос для синьоров
«Что происходит, когда горутина делает блокирующий syscall?»
Если горутина залипает в syscall, поток M блокируется вместе с ней. Чтобы не остановить весь Go-код, рантайм делает handoff P: отвязывает P от заблокированного M и переезжает на другой свободный M. Runnable горутины продолжают выполняться
Сколько OS threads может создать Go программа?
Тут надо отвечать двумя слоями, иначе выглядишь как человек, который прочитал одну статью на хабре и пришел на собес
Слой 1: есть лимит, по умолчанию 10 000 потоков, настраивается через runtime/debug.SetMaxThreads
Слой 2: Go создает новый поток НЕ потому что «надо больше параллелизма», а когда все существующие потоки заблокированы в syscalls, cgo или залочены через LockOSThread
Формулировка для собеса:
«GOMAXPROCS ограничивает число P и тем самым число потоков, одновременно выполняющих Go-код. Но общее число OS threads может вырасти из-за syscalls/cgo/LockOSThread и ограничено SetMaxThreads»
На этом тормозим
Во второй части разберем вытеснение (кооперативное vs асинхронное), что изменилось в Go 1.14, GOMAXPROCS в 2026 и контейнерах, а также топ-вопросы с собесов с готовыми ответами. Там будет самый сок
Вторая часть выйдет через несколько дней