Vai al contenuto
eLearner.app
Modulo 7 · Lezione 2 di 532/50 nel corso~15 min
Lezioni del modulo (2/5)

Channel: comunicazione tipata

I channel sono il meccanismo idiomatico di comunicazione tra goroutine. Filosofia Go (Rob Pike):

"Don't communicate by sharing memory; share memory by communicating."

Un channel è una coda FIFO tipata, type-safe e safe per uso concorrente. Combinato con go, è il building block primario della concorrenza Go.

Crea, invia, ricevi

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

Sintassi:

  • make(chan T) o make(chan T, N) per buffered.
  • ch <- v invia.
  • v := <-ch riceve.
  • v, ok := <-ch riceve con flag (ok = false se il canale è chiuso E vuoto).

Unbuffered: rendez-vous

Un canale unbuffered (capacità 0) blocca:

  • il sender finché un receiver non è pronto;
  • il receiver finché un sender non è pronto.

È un appuntamento sincrono: utile come "segnale" o per garantire che il valore sia stato consegnato.

Buffered: coda fino a 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

Il sender blocca solo quando il buffer è pieno; il receiver solo quando è vuoto.

close e for-range

Il sender chiude il canale quando non ci sono più valori:

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 su un canale itera finché il canale viene chiuso (e svuotato). È il pattern più pulito per "consumare tutto quello che arriva".

Direzionalità nelle firme

I parametri di funzione possono limitare la direzione:

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 = send-only, <-chan T = receive-only. Il canale "bidirezionale" chan T può essere passato a entrambi (conversione implicita). Aumenta la chiarezza e impedisce errori.

Pattern comuni (anteprima)

  • Worker pool: N goroutine consumano da jobs <-chan T, producono su results chan<- R.
  • Pipeline: stadi connessi da channel.
  • Done signal: canale chan struct{} chiuso per segnalare "stop".
Go
done := make(chan struct{})
go func() {
    <-done    // blocca finché qualcuno chiude done
    cleanup()
}()
close(done)   // segnale di stop

chan struct{} è il canale "segnale": nessun byte di payload, solo sincronizzazione.

Prova tu

Esercizio#go.m7.l2.e1
Tentativi: 0Caricamento…

Crea un channel ch di int unbuffered, invia 42 da una goroutine, ricevi in main e stampa.

Caricamento editor…
Mostra suggerimento

Senza buffer, sender e receiver si sincronizzano.

Soluzione disponibile dopo 3 tentativi

Esercizio#go.m7.l2.e2
Tentativi: 0Caricamento…

Invia 1,2,3 su un channel buffered, chiudilo, poi stampa tutto con for-range.

Caricamento editor…
Mostra suggerimento

`for range` su un canale termina quando il canale è chiuso e svuotato.

Soluzione disponibile dopo 3 tentativi

Quiz#go.m7.l2.e3
Pronto

Cosa fa un canale UNBUFFERED quando il sender invia ma nessun receiver è pronto?

Go
ch := make(chan int)
ch <- 1 // ?
Opzioni di risposta

Recap

  • Channel = coda FIFO tipata, safe fra goroutine.
  • Unbuffered: rendez-vous (sender e receiver si sincronizzano).
  • Buffered: disaccoppiamento, blocca solo quando pieno/vuoto.
  • close(ch): solo il sender; for range consuma fino al close.
  • Direzionalità: chan<- T (send-only), <-chan T (receive-only).
  • chan struct{} come canale-segnale per sincronizzazione pura.