Что такое горутина в Go и чем она отличается от системного потока (OS thread)?

Ответ

Горутина — это легковесная, управляемая средой выполнения (runtime) Go, функция или метод, выполняющийся конкурентно с другими горутинами. Это ключевая концепция для достижения конкурентности в Go.

Ключевые отличия от системных потоков:

  1. Стоимость создания и память:

    • Горутины: Очень дешевы. Начальный размер стека всего ~2 КБ, который может динамически расти и сжиматься. Можно легко запустить сотни тысяч и даже миллионы горутин.
    • Потоки ОС: Дорогие. Имеют фиксированный и значительно больший размер стека (обычно 1-8 МБ). Создание большого количества потоков сильно нагружает ОС.
  2. Управление и переключение контекста:

    • Горутины: Управляются планировщиком Go в пространстве пользователя (user space). Переключение между горутинами происходит очень быстро и не требует дорогостоящих системных вызовов.
    • Потоки ОС: Управляются ядром операционной системы. Переключение контекста между потоками — медленная операция.
  3. Модель выполнения (M:N):

    • Планировщик Go распределяет M горутин для выполнения на N системных потоках (где M обычно гораздо больше N). Это позволяет эффективно утилизировать процессорные ядра.

Идиоматичный пример с sync.WaitGroup:

Использование time.Sleep для синхронизации — плохая практика. Правильнее использовать примитивы синхронизации, например sync.WaitGroup.

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(1) // Сообщаем, что нужно дождаться одну горутину

    go func() {
        defer wg.Done() // Сигнализируем о завершении работы в конце функции
        fmt.Println("Горутина работает!")
        time.Sleep(time.Second)
    }()

    fmt.Println("Основная функция ждет завершения горутины...")
    wg.Wait() // Блокируем выполнение, пока счетчик WaitGroup не станет равен 0
    fmt.Println("Горутина завершила работу.")
}