Объясните модель G-M-P планировщика (scheduler) в Go

Ответ

Планировщик Go отвечает за распределение горутин по потокам операционной системы для выполнения. Он использует эффективную модель, известную как G-M-P.

Компоненты модели:

  1. G (Goroutine): Легковесный поток, управляемый рантаймом Go. Имеет свой небольшой стек, который может расти по мере необходимости. Это то, что мы создаем с помощью ключевого слова go.
  2. M (Machine): Поток операционной системы (kernel thread). Это реальный поток, который выполняет код. В программе может быть много M, но не все они могут быть активны одновременно.
  3. P (Processor): Контекст для выполнения. Он связывает горутины (G) с потоками ОС (M). Количество P по умолчанию равно количеству ядер CPU (GOMAXPROCS). У каждого P есть своя локальная очередь исполняемых горутин (LRQ - Local Run Queue).

Принцип работы (Work-Stealing):

  • Для выполнения работы поток M должен получить в распоряжение контекст P.
  • M берет горутину G из локальной очереди своего P и выполняет ее.
  • Если локальная очередь P пуста, P пытается "украсть" (work-stealing) половину горутин из очереди другого P или из глобальной очереди. Это обеспечивает равномерную загрузку всех потоков ОС.
  • Если горутина блокируется на системном вызове (например, чтение файла), M отсоединяется от P вместе с этой G, а рантайм может прикрепить к этому P другой M, чтобы продолжить выполнение других горутин.

Пример с sync.WaitGroup:

Для ожидания завершения горутин идиоматично использовать sync.WaitGroup, а не time.Sleep.

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1) // Увеличиваем счетчик WaitGroup
        go func(n int) {
            defer wg.Done() // Уменьшаем счетчик по завершении горутины
            fmt.Printf("Горутина %d выполняетсяn", n)
        }(i)
    }
    wg.Wait() // Ожидаем, пока счетчик не станет равен нулю
    fmt.Println("Все горутины завершили работу.")
}

С Go 1.14 планировщик является вытесняющим, что не позволяет одной горутине монополизировать поток выполнения надолго.