Когда треды ходят в глобальную очередь и берет от туда горутины?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда горутины используют глобальную очередь в Go?
В языке Go модель планирования горутин основана на концепции M:N планирования, где M горутин (пользовательские потоки) распределяются на N потоков операционной системы (OS threads). Для управления этим распределением используется планировщик Go (scheduler), который включает различные структуры данных, включая очереди.
Архитектура планировщика Go
Планировщик Go работает с тремя основными типами сущностей:
- G (goroutine): сами горутины, единицы выполнения.
- M (machine): поток ОС, который выполняет код.
- P (processor): логический процессор, контекст выполнения, который связывает M и G.
Каждый P имеет локальную очередь горутин (local runqueue), где хранятся готовые к выполнению горутины, связанные с этим конкретным P. Однако существует также глобальная очередь (global runqueue), доступная всем P.
Когда горутины попадают в глобальную очередь?
Горутины направляются в глобальную очередь в следующих ключевых случаях:
-
При создании новой горутины (
go func()) Когда запускается новая горутина черезgo, планировщик сначала пытается поместить её в локальную очередь текущего P. Если локальная очередь переполнена (более 256 горутин), половина горутин из локальной очереди перемещается в глобальную очередь, и новая горутина также может быть помещена туда.// Пример создания горутины go func() { fmt.Println("Эта горутина может попасть в глобальную очередь") }() -
При завершении работы горутины и возврате из системного вызова Если горутина завершает выполнение или возвращается из системного вызова (syscall), и связанный с ней M "засыпает", её можно переместить в глобальную очередь для балансировки нагрузки.
-
При балансировке нагрузки между P (work stealing) Планировщик периодически проверяет распределение нагрузки. Если у некоторых P локальные очереди пусты, они могут "воровать" (work stealing) горутины из глобальной очереди или из локальных очередей других P.
Когда горутины берутся из глобальной очереди?
Горутины извлекаются из глобальной очереди в следующих ситуациях:
-
Когда P ищет работу при пустой локальной очереди Если у P нет готовых к выполнению горутин в своей локальной очереди, он сначала проверяет глобальную очередь. Если там есть горутины, P забирает одну из них.
-
Периодическая проверка планировщика (every 61 tick) Планировщик работает с "тиками" (scheduler ticks). Каждый 61-й тик P проверяет глобальную очередь, чтобы обеспечить fairness и предотвратить "застревание" горутин в глобальной очереди.
// Внутренняя логика планировщика (символически) // if tick % 61 == 0 { // checkGlobalQueue() // } -
При системных вызовах и возврате M Когда M возвращается из системного вызова и пытается найти P для выполнения, если доступный P имеет пустую локальную очередь, он может обратиться к глобальной очереди.
Важность глобальной очереди
Глобальная очередь служит нескольким целям:
- Балансировка нагрузки: распределяет горутины между всеми P.
- Fairness: гарантирует, что горутины, попавшие в глобальную очередь, не будут забыты.
- Переполнение локальных очередей: действует как буфер при высокой нагрузке.
Таким образом, использование глобальной очереди в Go — это механизм обеспечения эффективного распределения работы между логическими процессорами, особенно в условиях динамически изменяющейся нагрузки и создания большого количества горутин. Она дополняет локальные очереди и является ключевым элементом для поддержания высокой производительности и справедливости в планировщике Go.