Что управляет выполнением горутин в Go?

Ответ

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

Планировщик работает по модели M:N, где:

  • G (Goroutine): Сама горутина. Включает в себя стек, указатель на текущую инструкцию и состояние. Горутины очень легковесны (начальный размер стека ~2 КБ).
  • M (Machine): Поток операционной системы (OS thread), управляемый ядром.
  • P (Processor): Логический процессор, представляющий собой контекст для выполнения горутин. Он связывает G и M. Количество P по умолчанию равно количеству ядер ЦП и может быть настроено через GOMAXPROCS.

Ключевые принципы работы планировщика:

  1. Work-Stealing (Воровство работы): Если у одного P заканчиваются горутины в локальной очереди, он может "украсть" половину горутин из очереди другого P. Это обеспечивает равномерную загрузку всех ядер.
  2. Кооперативность: Горутина уступает процессор другим горутинам во время вызовов функций, системных вызовов (I/O), ожидания на каналах или мьютексах. Это происходит без блокировки потока M.

Пример запуска горутины:

import (
    "fmt"
    "time"
)

func main() {
    // Запускаем новую горутину (G)
    go func() {
        // Этот код будет выполнен планировщиком параллельно с main
        fmt.Println("Hello from goroutine!")
    }()

    // Даем планировщику время на запуск горутины
    time.Sleep(time.Millisecond)
}

Таким образом, планировщик Go абстрагирует сложность управления потоками ОС, позволяя разработчику работать с простыми и легковесными горутинами.