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)
}