Lecții din modul (1/5)
Goroutine: paralelism ușor
O goroutine este o unitate de execuție simultană gestionată de Go
timpul de rulare. Lansați unul cu cuvântul cheie go în fața unei funcții
suna:
go greet("Anna") // chiamata a funzione esistente
go func() { // funzione anonima
fmt.Println("hi")
}()Instrucțiunea go revine imediat: rutina începe în
paralel și apelantul continuă.
De ce sunt „ușoare”
- Stiva inițială ~2KB (crește dinamic, până la sute de MB dacă este necesar).
- Multiplexat pe un număr mic de fire de execuție a sistemului de operare de către timpul de execuție (
GOMAXPROCS). - Mii/milioane de goroutine sunt de rutină; cu firele OS ar fi imposibile.
Conceptual este „foc concomitent și uita”, dar cu toate
instrumente idiomatice de coordonare (canale, sync.WaitGroup,
context).
principal nu așteaptă
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}Când main revine, procesul se încheie: goroutinele nu primesc
pentru a-și „termina munca”, sunt pur și simplu uciși împreună cu
proces. Este necesară sincronizarea explicită.
Sincronizare: o previzualizare
Cele trei instrumente pe care le veți vedea în următoarele lecții:
// 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 propagatiBug-ul „clasic” de închidere în buclă
Până la Go 1.21, aceasta a fost o problemă frecventă:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}De la Go 1.22 variabila for este proaspăt creată pentru fiecare
iterație, astfel încât eroarea este remediată implicit. În bazele de cod mai vechi veți
vezi în continuare modelul de remediere:
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 != Fir de operare
| Aspect | Goroutine | Firul de operare |
|---|---|---|
| Stiva inițială | ~2 KB | 1–8 MB (alocate la pornire) |
| Creație | nanosecunde | microsecunde |
| Programare | Go runtime (cooperativ) | nucleu (preemptiv) |
| Numărarea practică | 100K+ | câteva mii |
Un singur fir de operare poate găzdui multe goroutine. Timpul de rulare se mișcă goroutine prin fire atunci când este necesar.
Încearcă
Lansați funcția de salut ca o rutină și așteptați 100 ms cu timpul. Dormiți înainte de a ieși din principal.
Afișează indiciu
`go` în fața unui apel de funcție lansează goroutine.
Soluție disponibilă după 3 încercări
Lansați o funcție anonimă ca o rutină care afișează „bună”.
Afișează indiciu
O funcție anonimă este apelată cu un `()`: `go func(){...}()`.
Soluție disponibilă după 3 încercări
Ce se întâmplă dacă `principal` iese înainte de finalizarea unei rutine?
func main() {
go work()
// main ritorna immediatamente
}Recapitulare
go f()lansează f() simultan; se întoarce imediat.- Goroutine = ~2 KB inițial, multiplexat pe câteva fire de operare.
mainnu așteaptă: aveți nevoie de sincronizare (canal/WaitGroup/context).time.Sleepeste doar didactic, niciodată pentru sincronizare în producție.- Din Go 1.22, variabila
foreste „proaspătă” la fiecare iterație (nu mai există erori „3 3 3”).