Ответ вслух Senior Горутины и планировщик GMP обновлено

Как работает планировщик Go

Вопрос

Расскажите, как устроен планировщик горутин в Go. Что происходит при создании горутины, блокировке на канале, системном вызове и сетевом вводе-выводе?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

Планировщик Go реализует M:N: множество горутин G на нескольких потоках ОС M. Чтобы выполнять Go-код, поток должен держать P, логический процессор, их ровно GOMAXPROCS.

Что должно прозвучать в ответе

  • Модель G, M, P; число P = GOMAXPROCS; локальные очереди до 256 и глобальная

  • go f() кладёт G в runnext текущего P; переполнение уходит в глобальную

  • Блокировка на канале или мьютексе: G паркуется, M берёт следующую

  • Syscall: M блокируется, P передаётся другому M (handoff)

  • Сеть: неблокирующие сокеты, netpoller будит G

  • Work stealing, проверка глобальной очереди раз в 61 тик, асинхронное вытеснение с 1.14 через sysmon

Что могут спросить следом

  • Что делает sysmon?
  • Почему горутина дешевле потока, если в итоге всё равно выполняется на потоке?
  • Что будет, если GOMAXPROCS=1?

Разбор

Планировщик Go реализует M:N: множество горутин G на нескольких потоках ОС M. Чтобы выполнять Go-код, поток должен держать P, логический процессор, их ровно GOMAXPROCS. У каждого P есть локальная очередь на 256 горутин и слот runnext, плюс есть общая глобальная очередь. go f() создаёт G и кладёт в runnext текущего P, вытесненная оттуда уходит в локальную очередь, а при её переполнении половина переносится в глобальную. Когда горутина блокируется на канале, мьютексе или таймере, она паркуется в структуре ожидания, а M сразу берёт следующую G: переключение дешёвое, без ядра. Если горутина ушла в блокирующий syscall, P отвязывается и передаётся другому M, чтобы очередь не стояла. Сетевые операции не блокируют поток: сокеты неблокирующие, netpoller на epoll будит горутины по готовности. Когда работы нет, P смотрит глобальную очередь, netpoller и крадёт половину очереди у другого P. Раз в 61 тик планировщик проверяет глобальную очередь, чтобы она не голодала. Фоновый поток sysmon вытесняет горутины, работающие дольше 10 мс, сигналом (с Go 1.14), отбирает P у застрявших syscall и будит netpoller.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.