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
ch := make(chan int) // canale di int, unbuffered
go func() {
ch <- 42 // invia (può bloccare)
}()
v := <-ch // ricevi (può bloccare)
fmt.Println(v) // 42Sintassi:
make(chan T)omake(chan T, N)per buffered.ch <- vinvia.v := <-chriceve.v, ok := <-chriceve con flag (ok = falsese 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
ch := make(chan int, 3)
ch <- 1 // non blocca
ch <- 2 // non blocca
ch <- 3 // non blocca
ch <- 4 // BLOCCA: il buffer è pienoIl 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:
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:
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 suresults chan<- R. - Pipeline: stadi connessi da channel.
- Done signal: canale
chan struct{}chiuso per segnalare "stop".
done := make(chan struct{})
go func() {
<-done // blocca finché qualcuno chiude done
cleanup()
}()
close(done) // segnale di stopchan struct{} è il canale "segnale": nessun byte di payload, solo
sincronizzazione.
Prova tu
Crea un channel ch di int unbuffered, invia 42 da una goroutine, ricevi in main e stampa.
Mostra suggerimento
Senza buffer, sender e receiver si sincronizzano.
Soluzione disponibile dopo 3 tentativi
Invia 1,2,3 su un channel buffered, chiudilo, poi stampa tutto con for-range.
Mostra suggerimento
`for range` su un canale termina quando il canale è chiuso e svuotato.
Soluzione disponibile dopo 3 tentativi
Cosa fa un canale UNBUFFERED quando il sender invia ma nessun receiver è pronto?
ch := make(chan int)
ch <- 1 // ?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 rangeconsuma fino al close.- Direzionalità:
chan<- T(send-only),<-chan T(receive-only). chan struct{}come canale-segnale per sincronizzazione pura.