Direct naar de hoofdinhoud
Module 7 · Les 2 van 532/50 in de cursus~15 min
Modulelessen (2/5)

Kanalen: getypte communicatie

Kanalen zijn het idiomatische mechanisme voor communicatie tussen goroutines. De Go-filosofie (Rob Pike):

"Communiceer niet door herinneringen te delen; deel herinneringen door te communiceren."

Een kanaal is een getypeerde FIFO-wachtrij, typeveilig en veilig voor gelijktijdig gebruik gebruik. Gecombineerd met go is dit de belangrijkste bouwsteen van Go gelijktijdigheid.

Maken, verzenden, ontvangen

Go
ch := make(chan int)     // canale di int, unbuffered

go func() {
    ch <- 42             // invia (può bloccare)
}()

v := <-ch                // ricevi (può bloccare)
fmt.Println(v)           // 42

Syntaxis:

  • make(chan T) of make(chan T, N) voor gebufferd.
  • ch <- v verzendt.
  • v := <-ch ontvangt.
  • v, ok := <-ch ontvangt met een vlag (ok = false als het kanaal gesloten EN leeg is).

Ongebufferd: rendez-vous

Een ongebufferd kanaal (capaciteit 0) blokkeert:

  • de zender totdat een ontvanger gereed is;
  • de ontvanger totdat er een afzender klaar is.

Het is een synchrone afspraak: handig als ‘signaal’ of om te garanderen dat de waarde is geleverd.

Gebufferd: wachtrij tot N

Go
ch := make(chan int, 3)
ch <- 1   // non blocca
ch <- 2   // non blocca
ch <- 3   // non blocca
ch <- 4   // BLOCCA: il buffer è pieno

De afzender blokkeert alleen als de buffer vol is; alleen de ontvanger wanneer deze leeg is.

dichtbij en binnen bereik

De afzender sluit het kanaal als er geen waarden meer zijn:

Go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)

for v := range ch {      // termina quando ch è chiuso E vuoto
    fmt.Println(v)
}

for range over een kanaal herhaalt zich totdat het kanaal is gesloten (en leeggemaakt). Het is het schoonste patroon voor ‘consumeer alles wat binnenkomt’.

Directionaliteit in handtekeningen

Functieparameters kunnen de richting beperken:

Go
func producer(out chan<- int) {   // solo invio
    out <- 1
    close(out)
}

func consumer(in <-chan int) {    // solo ricezione
    for v := range in {
        fmt.Println(v)
    }
}

chan<- T = alleen verzenden, <-chan T = alleen ontvangen. Een bidirectioneel kanaal chan T kan aan beide worden doorgegeven (impliciete conversie). Het verbetert de duidelijkheid en voorkomt fouten.

Algemene patronen (preview)

  • Werknemerspool: N goroutines consumeren van jobs <-chan T, produceren op results chan<- R.
  • Pipeline: podia verbonden door kanalen.
  • Klaar signaal: een chan struct{} kanaal gesloten om "stop" aan te geven.
Go
done := make(chan struct{})
go func() {
    <-done    // blocca finché qualcuno chiude done
    cleanup()
}()
close(done)   // segnale di stop

chan struct{} is het signaalkanaal: nul bytes aan payload, slechts synchronisatie.

Probeer het

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

Maak een ongebufferd int-kanaalch, verzend 42 vanaf een goroutine, ontvang in het hoofdkanaal en druk het af.

Editor laden…
Toon hint

Zonder buffer synchroniseren zender en ontvanger.

Oplossing beschikbaar na 3 pogingen

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

Stuur 1,2,3 via een gebufferd kanaal, sluit het en druk alles af met for-range.

Editor laden…
Toon hint

'voor bereik' over een kanaal eindigt wanneer het kanaal gesloten en drooggelegd is.

Oplossing beschikbaar na 3 pogingen

Quiz#go.m7.l2.e3
Klaar

Wat doet een ONGEBUFFERD kanaal als de zender zendt maar er geen ontvanger gereed is?

Go
ch := make(chan int)
ch <- 1 // ?
Antwoordmogelijkheden

Samenvatten

  • Kanaal = getypte FIFO-wachtrij, veilig tussen goroutines.
  • Ongebufferd: rendez-vous (zender en ontvanger synchroniseren).
  • Gebufferd: ontkoppeling, blokkeert alleen bij vol/leeg.
  • close(ch): alleen de afzender; for range consumeert tot sluitingstijd.
  • Directionaliteit: chan<- T (alleen verzenden), <-chan T (alleen ontvangen).
  • chan struct{} als signaalkanaal voor pure synchronisatie.