Уроки модуля (1/5)
Горутины: легкий параллелизм
Горутина — это единица параллельного выполнения, управляемая Go.
время выполнения. Вы запускаете его с ключевым словом go перед функцией.
позвони:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()Оператор go возвращает немедленно: горутина запускается в
параллельно, и вызывающий абонент продолжает работать.
Почему они «легкие»
- Начальный стек ~2КБ (динамически увеличивается, при необходимости до сотен МБ).
- Мультиплексируется в небольшое количество потоков ОС средой выполнения (
GOMAXPROCS). - Тысячи/миллионы горутин являются рутинными; с потоками ОС это было бы невозможно.
Концептуально это принцип «выстрелил и забыл», но со всеми
идиоматические инструменты для координации (каналы, sync.WaitGroup,
context).
главный не ждет
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}Когда main возвращается, процесс завершается: горутины не получают
чтобы «закончить свое дело», их просто убивают вместе с
процесс. Требуется явная синхронизация.
Синхронизация: предварительный просмотр
Три инструмента, которые вы увидите в следующих уроках:
// 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 это часто встречалось:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}Начиная с Go 1.22, переменная for создается заново для каждого
итерации, поэтому ошибка исправлена по умолчанию. В старых кодовых базах вы будете
все еще вижу шаблон исправления:
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 тыс.+ | несколько тысяч |
В одном потоке ОС может размещаться множество горутин. Время выполнения перемещается горутины между потоками, когда это необходимо.
Попробуйте это
Запустите функцию приветствия как горутину и подождите 100 мс с помощью time.Sleep перед выходом из основной программы.
Показать подсказку
`go` перед вызовом функции запускает горутину.
Решение доступно после 3 попыток
Запустите анонимную функцию как горутину, которая печатает «привет».
Показать подсказку
Анонимная функция вызывается с завершающим `()`: `go func(){...}()`.
Решение доступно после 3 попыток
Что произойдет, если `main` завершит работу до завершения горутины?
func main() {
go work()
// main ritorna immediatamente
}Резюме
go f()одновременно запускает f(); он возвращается немедленно.- Goroutine = исходный код ~2 КБ, мультиплексированный в несколько потоков ОС.
mainне ждет: нужна синхронизация (канал/WaitGroup/контекст).time.Sleepносит только обучающий характер и никогда не предназначен для синхронизации в рабочей среде. — Начиная с Go 1.22 переменнаяforявляется «новой» на каждой итерации (ошибки «3 3 3» больше нет).