Горутинами управляет планировщик Go (scheduler), который является частью runtime-окружения языка. Это не планировщик операционной системы, а специальный механизм самого Go.
[Горутины (G)] --> [Процессоры (P)] --> [Потоки ОС (M)]
Планировщик использует модель M:N, где:
Гибридная многозадачность:
Кооперативная (основной режим):
runtime.Gosched()
Вытесняющая (добавлена в Go 1.14):
Пример вытеснения:
func longRunning() {
for i := 0; i < 1000000000; i++ {
// Долгие вычисления без точек вытеснения
}
}
// После 10мс выполнения планировщик может прервать эту горутину
Чисто кооперативная многозадачность:
Пример проблемы до 1.14:
func greedy() {
for {
// Без вызовов, которые передают управление
}
}
// В версиях до 1.14 эта горутина могла заблокировать весь поток
Инициализация:
Распределение:
Вытеснение:
SIGURG
для прерывания// Теперь безопасно делать:
go func() {
for {
heavyComputation() // Не заблокирует другие горутины надолго
}
}()
go func() {
for {
heavyComputation()
runtime.Gosched() // Явное указание планировщику
}
}()
GOMAXPROCS: Управляет количеством P
runtime.GOMAXPROCS(4) // 4 логических процессора
Отладка:
GODEBUG=schedtrace=1000 ./program # Трассировка планировщика
Эволюция планировщика Go сделала конкурентное программирование более надежным и предсказуемым.