В каких случаях планировщик Go использует глобальную очередь горутин?

Ответ

Планировщик Go построен на модели M:P:G, где:

  • M (Machine) — поток ОС.
  • P (Processor) — контекст для выполнения кода Go. У каждого P есть своя локальная очередь горутин (LRQ).
  • G (Goroutine) — горутина.

Основная цель планировщика — использовать локальные очереди (LRQ) как можно чаще, чтобы минимизировать блокировки и борьбу за общие ресурсы. Глобальная очередь (GRQ) является второстепенным механизмом.

Поток (M), связанный с процессором (P), ищет горутины для выполнения в следующем порядке:


  1. Локальная очередь (LRQ): Сначала P пытается взять горутину из своей собственной локальной очереди. Это самый быстрый и предпочтительный путь.



  2. Глобальная очередь (GRQ): Если LRQ пуста, P проверяет глобальную очередь. Горутины попадают в GRQ в основном тогда, когда они становятся готовыми к выполнению, но не имеют привязки к конкретному P. Например, после завершения системного вызова или срабатывания таймера.



  3. Сетевой поллер (Network Poller): Если и GRQ пуста, P проверяет сетевой поллер на наличие горутин, которые были заблокированы на сетевом вводе-выводе и теперь готовы к работе.



  4. "Воровство" работы (Work Stealing): В последнюю очередь, если все источники пусты, P пытается "украсть" половину горутин из локальной очереди другого, случайным образом выбранного P.


Вывод: Глобальная очередь используется как один из резервных источников работы, когда локальная очередь пуста. Она помогает сбалансировать нагрузку и задействовать горутины, которые были разблокированы системными событиями, но не являются основным механизмом распределения задач, в отличие от локальных очередей.