Перейти к основному содержимому
Модуль 7 · Урок 1 из 531/50 в курсе~12 min
Уроки модуля (1/5)

Горутины: легкий параллелизм

Горутина — это единица параллельного выполнения, управляемая Go. время выполнения. Вы запускаете его с ключевым словом go перед функцией. позвони:

Go
go greet("Anna")         // chiamata a funzione esistente
go func() {              // funzione anonima
    fmt.Println("hi")
}()

Оператор go возвращает немедленно: горутина запускается в параллельно, и вызывающий абонент продолжает работать.

Почему они «легкие»

  • Начальный стек ~2КБ (динамически увеличивается, при необходимости до сотен МБ).
  • Мультиплексируется в небольшое количество потоков ОС средой выполнения (GOMAXPROCS).
  • Тысячи/миллионы горутин являются рутинными; с потоками ОС это было бы невозможно.

Концептуально это принцип «выстрелил и забыл», но со всеми идиоматические инструменты для координации (каналы, sync.WaitGroup, context).

главный не ждет

Go
func main() {
    go func() { fmt.Println("ciao dalla goroutine") }()
    // main esce subito: niente garanzia che la goroutine completi
}

Когда main возвращается, процесс завершается: горутины не получают чтобы «закончить свое дело», их просто убивают вместе с процесс. Требуется явная синхронизация.

Синхронизация: предварительный просмотр

Три инструмента, которые вы увидите в следующих уроках:

Go
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
    work()
    done <- struct{}{}
}()
<-done   // attendi

// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()

// 3) context: cancellazione/timeout propagati

«Классическая» ошибка замыкания в цикле

До Go 1.21 это часто встречалось:

Go
for i := 0; i < 3; i++ {
    go func() { fmt.Println(i) }()  // stampa "3 3 3" su versioni vecchie
}

Начиная с Go 1.22, переменная for создается заново для каждого итерации, поэтому ошибка исправлена по умолчанию. В старых кодовых базах вы будете все еще вижу шаблон исправления:

Go
for i := 0; i < 3; i++ {
    i := i           // shadow esplicito
    go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
    go func(n int) { fmt.Println(n) }(i)
}

Горутина != поток ОС

АспектГорутинаВетка ОС
Начальный стек~2 КБ1–8 МБ (выделяется при запуске)
Созданиенаносекундымикросекунды
ПланированиеGo runtime (кооператив)ядро (вытесняющее)
Практический подсчет100 тыс.+несколько тысяч

В одном потоке ОС может размещаться множество горутин. Время выполнения перемещается горутины между потоками, когда это необходимо.

Попробуйте это

Упражнение#go.m7.l1.e1
Попыток: 0Загрузка…

Запустите функцию приветствия как горутину и подождите 100 мс с помощью time.Sleep перед выходом из основной программы.

Загрузка редактора…
Показать подсказку

`go` перед вызовом функции запускает горутину.

Решение доступно после 3 попыток

Упражнение#go.m7.l1.e2
Попыток: 0Загрузка…

Запустите анонимную функцию как горутину, которая печатает «привет».

Загрузка редактора…
Показать подсказку

Анонимная функция вызывается с завершающим `()`: `go func(){...}()`.

Решение доступно после 3 попыток

Quiz#go.m7.l1.e3
Готово

Что произойдет, если `main` завершит работу до завершения горутины?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Варианты ответа

Резюме

  • go f() одновременно запускает f(); он возвращается немедленно.
  • Goroutine = исходный код ~2 КБ, мультиплексированный в несколько потоков ОС.
  • main не ждет: нужна синхронизация (канал/WaitGroup/контекст).
  • time.Sleep носит только обучающий характер и никогда не предназначен для синхронизации в рабочей среде. — Начиная с Go 1.22 переменная for является «новой» на каждой итерации (ошибки «3 3 3» больше нет).