Как устроены и работают горутины в Go?

Ответ

Горутины — это легковесные потоки исполнения, которые управляются не операционной системой, а средой выполнения (runtime) Go. Их ключевое преимущество — низкое потребление ресурсов (начальный размер стека ~2 КБ) и эффективное переключение контекста.

Модель планировщика M:P:G

В основе работы горутин лежит модель планировщика M:P:G:

  • G (Goroutine): Сама горутина со своим стеком, указателем на текущую инструкцию и состоянием.
  • M (Machine): Системный поток ОС (OS thread). Это реальный поток, который выполняет код.
  • P (Processor): Контекст для выполнения Go-кода. Он представляет собой ресурс для запуска горутин и имеет локальную очередь горутин (LRQ), готовых к выполнению. Количество P по умолчанию равно количеству ядер CPU (GOMAXPROCS).

Процесс работы: Планировщик Go стремится, чтобы на каждом системном потоке M был запущен контекст P, который, в свою очередь, выполняет горутины G из своей локальной очереди. Это позволяет мультиплексировать тысячи горутин на небольшом количестве системных потоков.

Ключевые аспекты

  • Запуск: Горутина создается с помощью ключевого слова go.
    go func() {
        fmt.Println("Я — горутина!")
    }()
  • Конкурентность: Горутины выполняются конкурентно. Для достижения параллелизма (одновременного выполнения на разных ядрах) GOMAXPROCS должен быть больше 1.
  • Синхронизация: Для безопасного обмена данными между горутинами используются каналы (chan). Для синхронизации выполнения — примитивы из пакета sync (WaitGroup, Mutex).
  • Завершение: Если главная горутина (main) завершает свою работу, все остальные горутины принудительно останавливаются.