Timeouts
Per evitare lo spinning all'infinito o il blocco nel main si può usare un timeout.
Questo è prodotto dalla funzione After
del package time
che prende come argomento un intervallo di tempo e pone un valore su un suo channel predefinito allo scadere dell'intervallo.
Il programma seguente illustra i timeout con due esempi.
Nel primo esempio arriva prima il timeout della goroutine, nel secondo caso prima la goroutine del timeout.
Esempio
(280timeouts.go):
package main
import "time"
import "fmt"
func main() {
// Goroutine che comunica su channel dopo 2s
c1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
// timeout dopo un secondo
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
// Goroutine che comunica su channel dopo 2s
c2 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
c2 <- "result 2"
}()
select {
case res := <-c2:
fmt.Println(res)
// timeout dopo 3 secondi
case <-time.After(time.Second * 3):
fmt.Println("timeout 2")
}
fmt.Println("Terminated")
}
Break ad etichetta
Supponiamo di sostituire l'ultimo select
del secondo esempio con:
for {
select {
case res := <-c2:
fmt.Println(res)
// timeout dopo 3 secondi
case <-time.After(time.Second * 3):
fmt.Println("timeout 2")
}
}
Il programma segnala correttamente result 2
ma poi torna in loop infinito a dire timeout 2
ogni 3 secondi. Occorre uscire dal for dopo l'uno o l'altro case, ma non si può usare return
poichè uscirebbe dall'intero programma e non stamperebbe Terminated.
Se si usa il break
come nel frammento seguente:
for {
select {
case res := <-c2:
fmt.Println(res)
break
// timeout dopo 3 secondi
case <-time.After(time.Second * 3):
fmt.Println("timeout 2")
break
}
}
fmt.Println("Terminated")
Il risultato non cambia perchè break esce solo dal costrutto corrente, che è il select
, non il for
.
La soluzione è un break a label:
loop:
for {
select {
case res := <-c2:
fmt.Println(res)
break loop
// timeout dopo 3 secondi
case <-time.After(time.Second * 3):
fmt.Println("timeout 2")
break loop
}
}
fmt.Println("Terminated")
loop:
è un'etichetta (una stringa seguita da due punti), che marca il costrutto seguente.
break loop
causa l'uscita dal costrutto marcato con l'etichetta loop.
Notare che non si tratta di un goto
come in linguaggio C, ma di qualcosa di più complesso.
Un costrutto molto simile esiste anche in Java.