Direct naar de hoofdinhoud
Module 7 · Les 1 van 531/50 in de cursus~12 min
Modulelessen (1/5)

Goroutines: lichtgewicht parallellisme

Een goroutine is een eenheid voor gelijktijdige uitvoering die wordt beheerd door de Go looptijd. Je start er een met het trefwoord go vóór een functie bel:

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

De instructie go retourneert onmiddellijk: de goroutine begint over parallel en de beller blijft doorgaan.

Waarom ze "lichtgewicht" zijn

  • Initiële stapel ~2 KB (groeit dynamisch, tot honderden MB indien nodig).
  • Gemultiplext op een klein aantal OS-threads tijdens de runtime (GOMAXPROCS).
  • Duizenden/miljoenen goroutines zijn routine; met OS-threads zouden ze onmogelijk zijn.

Conceptueel gezien is het "gelijktijdig vuur-en-vergeet", maar met alle idiomatische hulpmiddelen om te coördineren (kanalen, sync.WaitGroup, context).

Hoofd wacht niet

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

Wanneer main terugkeert, wordt het proces beëindigd: goroutines krijgen geen om "hun werk af te maken", worden ze eenvoudigweg samen met de vermoord proces. Expliciete synchronisatie is vereist.

Synchronisatie: een voorbeeld

De drie tools die je in de volgende lessen zult zien:

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

De "klassieke" sluiting-in-lus-bug

Tot Go 1.21 was dit een vaak voorkomend probleem:

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

Vanaf Go 1.22 wordt de variabele for voor elke versie opnieuw aangemaakt iteratie, dus de bug is standaard opgelost. In oudere codebases wel zie nog steeds het fixpatroon:

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 != OS-thread

AspectGoroutineOS-thread
Initiële stapel~2 KB1–8 MB (toegewezen bij start)
Creatienanosecondenmicroseconden
PlanningGa runtime (coöperatief)kernel (preventief)
Praktisch tellen100K+een paar duizend

Eén enkele OS-thread kan vele goroutines hosten. De looptijd beweegt goroutines over threads heen wanneer dat nodig is.

Probeer het

Oefening#go.m7.l1.e1
Pogingen: 0Laden…

Start de begroetingsfunctie als een goroutine en wacht 100 ms met de tijd. Slaap voordat u de hoofdmodus verlaat.

Editor laden…
Toon hint

`go` voor een functieaanroep start de goroutine.

Oplossing beschikbaar na 3 pogingen

Oefening#go.m7.l1.e2
Pogingen: 0Laden…

Start een anonieme functie als een goroutine die 'hallo' afdrukt.

Editor laden…
Toon hint

Een anonieme functie wordt aangeroepen met een vervolg `()`: `go func(){...}()`.

Oplossing beschikbaar na 3 pogingen

Quiz#go.m7.l1.e3
Klaar

Wat gebeurt er als 'main' wordt afgesloten voordat een goroutine is voltooid?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
Antwoordmogelijkheden

Samenvatten

  • go f() start f() gelijktijdig; het keert onmiddellijk terug.
  • Goroutine = ~2 KB aanvankelijk, gemultiplext op een paar OS-threads.
  • main wacht niet: je hebt synchronisatie nodig (kanaal/WaitGroup/context).
  • time.Sleep is alleen didactisch, nooit voor synchronisatie in productie.
  • Vanaf Go 1.22 is de variabele for bij elke iteratie "vers" (geen "3 3 3"-bug meer).