← Назад к вопросам

Когда треды ходят в глобальную очередь и берет от туда горутины?

2.0 Middle🔥 131 комментариев
#Другое#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда горутины используют глобальную очередь в 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.

Когда горутины попадают в глобальную очередь?

Горутины направляются в глобальную очередь в следующих ключевых случаях:

  1. При создании новой горутины (go func()) Когда запускается новая горутина через go, планировщик сначала пытается поместить её в локальную очередь текущего P. Если локальная очередь переполнена (более 256 горутин), половина горутин из локальной очереди перемещается в глобальную очередь, и новая горутина также может быть помещена туда.

    // Пример создания горутины
    go func() {
        fmt.Println("Эта горутина может попасть в глобальную очередь")
    }()
    
  2. При завершении работы горутины и возврате из системного вызова Если горутина завершает выполнение или возвращается из системного вызова (syscall), и связанный с ней M "засыпает", её можно переместить в глобальную очередь для балансировки нагрузки.

  3. При балансировке нагрузки между P (work stealing) Планировщик периодически проверяет распределение нагрузки. Если у некоторых P локальные очереди пусты, они могут "воровать" (work stealing) горутины из глобальной очереди или из локальных очередей других P.

Когда горутины берутся из глобальной очереди?

Горутины извлекаются из глобальной очереди в следующих ситуациях:

  1. Когда P ищет работу при пустой локальной очереди Если у P нет готовых к выполнению горутин в своей локальной очереди, он сначала проверяет глобальную очередь. Если там есть горутины, P забирает одну из них.

  2. Периодическая проверка планировщика (every 61 tick) Планировщик работает с "тиками" (scheduler ticks). Каждый 61-й тик P проверяет глобальную очередь, чтобы обеспечить fairness и предотвратить "застревание" горутин в глобальной очереди.

    // Внутренняя логика планировщика (символически)
    // if tick % 61 == 0 {
    //     checkGlobalQueue()
    // }
    
  3. При системных вызовах и возврате M Когда M возвращается из системного вызова и пытается найти P для выполнения, если доступный P имеет пустую локальную очередь, он может обратиться к глобальной очереди.

Важность глобальной очереди

Глобальная очередь служит нескольким целям:

  • Балансировка нагрузки: распределяет горутины между всеми P.
  • Fairness: гарантирует, что горутины, попавшие в глобальную очередь, не будут забыты.
  • Переполнение локальных очередей: действует как буфер при высокой нагрузке.

Таким образом, использование глобальной очереди в Go — это механизм обеспечения эффективного распределения работы между логическими процессорами, особенно в условиях динамически изменяющейся нагрузки и создания большого количества горутин. Она дополняет локальные очереди и является ключевым элементом для поддержания высокой производительности и справедливости в планировщике Go.