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 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
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:
// 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 propagatiDe "klassieke" sluiting-in-lus-bug
Tot Go 1.21 was dit een vaak voorkomend probleem:
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:
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
| Aspect | Goroutine | OS-thread |
|---|---|---|
| Initiële stapel | ~2 KB | 1–8 MB (toegewezen bij start) |
| Creatie | nanoseconden | microseconden |
| Planning | Ga runtime (coöperatief) | kernel (preventief) |
| Praktisch tellen | 100K+ | 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
Start de begroetingsfunctie als een goroutine en wacht 100 ms met de tijd. Slaap voordat u de hoofdmodus verlaat.
Toon hint
`go` voor een functieaanroep start de goroutine.
Oplossing beschikbaar na 3 pogingen
Start een anonieme functie als een goroutine die 'hallo' afdrukt.
Toon hint
Een anonieme functie wordt aangeroepen met een vervolg `()`: `go func(){...}()`.
Oplossing beschikbaar na 3 pogingen
Wat gebeurt er als 'main' wordt afgesloten voordat een goroutine is voltooid?
func main() {
go work()
// main ritorna immediatamente
}Samenvatten
go f()start f() gelijktijdig; het keert onmiddellijk terug.- Goroutine = ~2 KB aanvankelijk, gemultiplext op een paar OS-threads.
mainwacht niet: je hebt synchronisatie nodig (kanaal/WaitGroup/context).time.Sleepis alleen didactisch, nooit voor synchronisatie in productie.- Vanaf Go 1.22 is de variabele
forbij elke iteratie "vers" (geen "3 3 3"-bug meer).