Module lessons (2/5)
Channels: typed communication
Channels are the idiomatic mechanism for communication between goroutines. The Go philosophy (Rob Pike):
"Don't communicate by sharing memory; share memory by communicating."
A channel is a typed FIFO queue, type-safe and safe for concurrent
use. Combined with go, it is the primary building block of Go
concurrency.
Create, send, receive
ch := make(chan int) // canale di int, unbuffered
go func() {
ch <- 42 // invia (può bloccare)
}()
v := <-ch // ricevi (può bloccare)
fmt.Println(v) // 42Syntax:
make(chan T)ormake(chan T, N)for buffered.ch <- vsends.v := <-chreceives.v, ok := <-chreceives with a flag (ok = falseif the channel is closed AND empty).
Unbuffered: rendezvous
An unbuffered channel (capacity 0) blocks:
- the sender until a receiver is ready;
- the receiver until a sender is ready.
It is a synchronous appointment: useful as a "signal" or to guarantee that the value has been delivered.
Buffered: queue up to N
ch := make(chan int, 3)
ch <- 1 // non blocca
ch <- 2 // non blocca
ch <- 3 // non blocca
ch <- 4 // BLOCCA: il buffer è pienoThe sender blocks only when the buffer is full; the receiver only when it is empty.
close and for-range
The sender closes the channel when there are no more values:
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 a channel iterates until the channel is closed (and drained).
It is the cleanest pattern for "consume everything that arrives".
Directionality in signatures
Function parameters can restrict the direction:
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. A bidirectional
channel chan T can be passed to either (implicit conversion). It
improves clarity and prevents mistakes.
Common patterns (preview)
- Worker pool: N goroutines consume from
jobs <-chan T, produce onresults chan<- R. - Pipeline: stages connected by channels.
- Done signal: a
chan struct{}channel closed to signal "stop".
done := make(chan struct{})
go func() {
<-done // blocca finché qualcuno chiude done
cleanup()
}()
close(done) // segnale di stopchan struct{} is the "signal" channel: zero bytes of payload, just
synchronization.
Try it
Create an unbuffered int channel ch, send 42 from a goroutine, receive in main and print it.
Show hint
Without a buffer, sender and receiver synchronize.
Solution available after 3 attempts
Send 1,2,3 over a buffered channel, close it, then print everything with for-range.
Show hint
`for range` over a channel ends when the channel is closed and drained.
Solution available after 3 attempts
What does an UNBUFFERED channel do when the sender sends but no receiver is ready?
ch := make(chan int)
ch <- 1 // ?Recap
- Channel = typed FIFO queue, safe between goroutines.
- Unbuffered: rendezvous (sender and receiver synchronize).
- Buffered: decoupling, blocks only when full/empty.
close(ch): only the sender;for rangeconsumes until close.- Directionality:
chan<- T(send-only),<-chan T(receive-only). chan struct{}as a signal channel for pure synchronization.