Salt la conținutul principal
Modulul 7 · Lecția 1 din 531/50 în curs~12 min
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
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ă

Go
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:

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

Bug-ul „clasic” de închidere în buclă

Până la Go 1.21, aceasta a fost o problemă frecventă:

Go
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:

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)
}

Goroutine != Fir de operare

AspectGoroutineFirul de operare
Stiva inițială~2 KB1–8 MB (alocate la pornire)
Creațienanosecundemicrosecunde
ProgramareGo 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ă

Exercițiu#go.m7.l1.e1
Încercări: 0Se încarcă…

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.

Se încarcă editorul...
Afișează indiciu

`go` în fața unui apel de funcție lansează goroutine.

Soluție disponibilă după 3 încercări

Exercițiu#go.m7.l1.e2
Încercări: 0Se încarcă…

Lansați o funcție anonimă ca o rutină care afișează „bună”.

Se încarcă editorul...
Afișează indiciu

O funcție anonimă este apelată cu un `()`: `go func(){...}()`.

Soluție disponibilă după 3 încercări

Quiz#go.m7.l1.e3
Gata

Ce se întâmplă dacă `principal` iese înainte de finalizarea unei rutine?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Opțiuni de răspuns

Recapitulare

  • go f() lansează f() simultan; se întoarce imediat.
  • Goroutine = ~2 KB inițial, multiplexat pe câteva fire de operare.
  • main nu așteaptă: aveți nevoie de sincronizare (canal/WaitGroup/context).
  • time.Sleep este doar didactic, niciodată pentru sincronizare în producție.
  • Din Go 1.22, variabila for este „proaspătă” la fiecare iterație (nu mai există erori „3 3 3”).