Range sui Channel
Per automatizzare la detezione della chiusura del channel si usa il costrutto range
che opera su un channel. Questo costrutto ascolta in continuazione sul channel ma si accorge della presenza del segnale di chiusura ed esce.
Se il channel non fosse stato chiuso si sarebbe andati ad un blocco.
Esempio
(310range-over-channels.go):
package main
import "fmt"
func main() {
// Due valori in un channel
queue := make(chan string, 2)
// Il channel bufferizzato deve avere capacità
// per tutti i valori che riceve
// Il seguente codice produce un blocco
// queue := make(chan string, 1)
queue <- "one"
queue <- "two"
// Il channel viene esplicitamente chiuso
close(queue)
// Scandisce tutti gli elementi del channel
// Se non venisse chiuso vi sarebbe un blocco
for elem := range queue {
fmt.Println(elem)
}
}
// In generale i channel sono concepiti per comunicazione
// fra thread diversi. Comunque occorre sempre
// preoccuparsi di evitare i deadlock
range
richiede che il channel abbia avuto un close
.
Esempio 2
(311chrange.go):
package main
import "fmt"
// funzione di goroutine che comunica tramite un channel
func FibonacciProducer(ch chan int, count int) {
n2, n1 := 0, 1
for count >= 0 {
// un elemento per volta nel channel
ch <- n2
count--
n2, n1 = n1, n2+n1
}
// è indispensaabile chiudere il channel
// o si verifica un deadlock
close(ch)
}
func main() {
// non è necessario un channel bufferizzato
ch := make(chan int)
go FibonacciProducer(ch, 10)
idx := 0
// range si ferma quando incontra il
// marker di chiusura del channel
for num := range ch {
fmt.Printf("F(%d): \t%d\n", idx, num)
idx++
}
}
Si può usare range anche su un channel unbuffered.
Questo codice non è consentito:
ch := make(chan int)
go FibonacciProducer(ch, 10)
for idx, num := range ch {
fmt.Printf("idx: %d F(%d): \t%d\n", idx, num)
}
Se l’espressione del range è un channel si può avere al massimo una variabile di iterazione, altrimenti fino a due.
Esempio 3
(312norange.go);:
package main
import (
"fmt"
"time"
)
func readMessages(messenger <-chan string) {
// contatore, scopo didattico
i := 0
for {
select {
case msg := <-messenger:
fmt.Println(msg)
default:
// stampa e incrementa il contatore
fmt.Println(i, ": no message")
i++
}
}
}
func main() {
mess := make(chan string)
go readMessages(mess)
mess <- "hello"
close(mess)
// simula altro lavoro
time.Sleep(time.Millisecond)
}
Esempio 4
(313withrange.go):
package main
import (
"fmt"
"time"
)
func readMessages(messenger <-chan string) {
// legge solo se c’è un valore nel channel
// si ferma quando il channel è chiuso
for msg := range messenger {
fmt.Println(msg)
}
}
func main() {
mess := make(chan string)
go readMessages(mess)
mess <- "hello"
close(mess)
// simula altro lavoro
time.Sleep(time.Millisecond)
}