Вопрос проверяет понимание внутреннего устройства рантайма Go, а именно планировщика горутин и GMP модели, что необходимо для написания эффективного конкурентного кода.
GMP — это аббревиатура, описывающая три ключевых компонента планировщика Go: G (goroutine) — легковесный поток выполнения, M (machine) — поток операционной системы, P (processor) — логический процессор, который управляет очередью горутин и связывает G с M. Планировщик Go работает на уровне пользовательского пространства, что делает переключение между горутинами гораздо дешевле, чем переключение потоков ОС.
Каждый P имеет локальную очередь горутин. Когда горутина блокируется (например, на системном вызове или канале), M может переключиться на другую горутину из очереди P. Если очередь P пуста, планировщик может украсть горутину из очереди другого P (work stealing). Это обеспечивает равномерную загрузку всех ядер процессора.
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
// Устанавливаем количество логических процессоров
runtime.GOMAXPROCS(2)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Горутина %d\n", id)
}(i)
}
wg.Wait()
}В этом примере мы создаём 10 горутин, которые планировщик распределяет по 2 логическим процессорам. Каждая горутина выполняется конкурентно, а планировщик автоматически управляет их переключением.
GMP модель позволяет Go эффективно управлять тысячами и миллионами горутин на ограниченном числе потоков ОС, что делает язык идеальным для высоконагруженных серверных приложений и микросервисов.