Ответ
Выполнением горутин управляет встроенный планировщик Go (Go scheduler), который является частью рантайма языка. Он реализует кооперативную многозадачность, эффективно распределяя множество горутин по небольшому количеству потоков операционной системы.
Планировщик работает по модели M:N, где:
- G (Goroutine): Сама горутина. Включает в себя стек, указатель на текущую инструкцию и состояние. Горутины очень легковесны (начальный размер стека ~2 КБ).
- M (Machine): Поток операционной системы (OS thread), управляемый ядром.
- P (Processor): Логический процессор, представляющий собой контекст для выполнения горутин. Он связывает
G
иM
. КоличествоP
по умолчанию равно количеству ядер ЦП и может быть настроено черезGOMAXPROCS
.
Ключевые принципы работы планировщика:
- Work-Stealing (Воровство работы): Если у одного
P
заканчиваются горутины в локальной очереди, он может "украсть" половину горутин из очереди другогоP
. Это обеспечивает равномерную загрузку всех ядер. - Кооперативность: Горутина уступает процессор другим горутинам во время вызовов функций, системных вызовов (I/O), ожидания на каналах или мьютексах. Это происходит без блокировки потока
M
.
Пример запуска горутины:
import (
"fmt"
"time"
)
func main() {
// Запускаем новую горутину (G)
go func() {
// Этот код будет выполнен планировщиком параллельно с main
fmt.Println("Hello from goroutine!")
}()
// Даем планировщику время на запуск горутины
time.Sleep(time.Millisecond)
}
Таким образом, планировщик Go абстрагирует сложность управления потоками ОС, позволяя разработчику работать с простыми и легковесными горутинами.