Ответ
Планировщик Go использует две основные очереди для управления горутинами: локальную и глобальную.
1. Локальная очередь (LRQ - Local Run Queue)
- Привязка: Каждому
P
(процессору/контексту выполнения) принадлежит своя локальная очередь. - Назначение: Это основная очередь, из которой
P
берет горутины для выполнения. Такой подход минимизирует блокировки и contention, так как каждыйP
работает со своей очередью. - Ограничение: Размер LRQ ограничен (обычно 256 горутин). Если при создании новой горутины LRQ переполнена, половина горутин из нее перемещается в глобальную очередь.
2. Глобальная очередь (GRQ - Global Run Queue)
- Назначение: Это общая, единая для всех
P
очередь. Она служит в качестве "резерва". - Когда горутины попадают в GRQ?
- При переполнении локальной очереди (LRQ).
- Горутины, которые были заблокированы на системном вызове и теперь снова готовы к выполнению.
- Иногда — новые созданные горутины.
Механизм "Work-Stealing" (Воровство работы)
Это ключевой механизм балансировки нагрузки:
P
в первую очередь ищет горутины в своей локальной очереди (LRQ).- Если LRQ пуста,
P
пытается найти работу в следующем порядке:- Проверяет глобальную очередь (GRQ).
- Если GRQ пуста,
P
становится "вором": он пытается "украсть" половину горутин из LRQ другого, случайногоP
. - Если и это не удалось,
P
проверяет сетевой поллер на наличие готовых к выполнению горутин (ожидающих I/O).
Этот механизм позволяет эффективно распределять работу и избегать простоя системных потоков (M
).