Ответ
Планировщик Go построен на модели M:P:G, где:
- M (Machine) — поток ОС.
- P (Processor) — контекст для выполнения кода Go. У каждого P есть своя локальная очередь горутин (LRQ).
- G (Goroutine) — горутина.
Основная цель планировщика — использовать локальные очереди (LRQ) как можно чаще, чтобы минимизировать блокировки и борьбу за общие ресурсы. Глобальная очередь (GRQ) является второстепенным механизмом.
Поток (M), связанный с процессором (P), ищет горутины для выполнения в следующем порядке:
Локальная очередь (LRQ): Сначала P пытается взять горутину из своей собственной локальной очереди. Это самый быстрый и предпочтительный путь.
Глобальная очередь (GRQ): Если LRQ пуста, P проверяет глобальную очередь. Горутины попадают в GRQ в основном тогда, когда они становятся готовыми к выполнению, но не имеют привязки к конкретному P. Например, после завершения системного вызова или срабатывания таймера.
Сетевой поллер (Network Poller): Если и GRQ пуста, P проверяет сетевой поллер на наличие горутин, которые были заблокированы на сетевом вводе-выводе и теперь готовы к работе.
"Воровство" работы (Work Stealing): В последнюю очередь, если все источники пусты, P пытается "украсть" половину горутин из локальной очереди другого, случайным образом выбранного P.
Вывод: Глобальная очередь используется как один из резервных источников работы, когда локальная очередь пуста. Она помогает сбалансировать нагрузку и задействовать горутины, которые были разблокированы системными событиями, но не являются основным механизмом распределения задач, в отличие от локальных очередей.