Модульні уроки (1/5)
Goroutines: легкий паралелізм
goroutine — це одиниця одночасного виконання, якою керує Go
час виконання. Ви запускаєте його з ключовим словом go перед функцією
дзвонити:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()Інструкція go повертає негайно: горутина починається в
паралельно, і абонент продовжує йти.
Чому вони «легкі»
- Початковий стек ~2 КБ (динамічно зростає, за потреби до сотень МБ).
- Мультиплексований на невелику кількість потоків ОС під час виконання (
GOMAXPROCS). - Тисячі/мільйони goroutines є рутиною; з потоками ОС вони були б неможливі.
Концептуально це «одночасний вогонь і забув», але з усім
ідіоматичні інструменти для координації (канали, 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)
}Goroutine != Потік ОС
| Аспект | Goroutine | Потік ОС |
|---|---|---|
| Початковий стек | ~2 КБ | 1–8 МБ (виділено на початку) |
| Створення | наносекунд | мікросекунд |
| Планування | Go runtime (кооператив) | ядро (превентивне) |
| Практичний підрахунок | 100 тисяч+ | кілька тисяч |
Один потік ОС може розміщувати багато gor-програм. Час виконання рухається goroutines через потоки, коли це необхідно.
Спробуйте
Запустіть функцію привітання як goroutine та зачекайте 100 мс із часом. Перейдіть у режим сну перед виходом з основного.
Показати підказку
`go` перед викликом функції запускає gor-програму.
Рішення доступне після 3 спроб
Запустіть анонімну функцію як goroutine, яка друкує «привіт».
Показати підказку
Анонімна функція викликається з `()` в кінці: `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»).