Timers
Un timer è una struct complessa, comprendente un campo channel C
.
Il timer viene creato con la funzione NewTimer
del package time, che lo alloca in memoria dinamica.
Il parametro di creazione è un tempo, allo scadere del quale viene posto un valore sul channel C
del timer.
Anche la funzione After
del package time si comporta in modo simile, ma sottilmente diverso. After
è un channel che viene riempito allo scadere del suo argomento.
Mentre un timer è interrompibile, il canale After non lo è. After
serve per un ritardo temporale locale, un timer
è una sveglia assoluta.
Esempio
(320timers.go):
package main
import "time"
import "fmt"
func main() {
// Un timer è un evento a scadenza temporale futura
// Un timer ha un channel C che ha un elemento
// allo scadere del timer
timer1 := time.NewTimer(time.Second)
fmt.Println("main: timer 1 set to 1 second")
// La seguente è bloccante
<-timer1.C
fmt.Println("main: timer 1 expired")
// Un timer può essere fermato prima dello scadere
// a differenza di time.Sleep
timer2 := time.NewTimer(10 * time.Second)
fmt.Println("main: timer 2 set to 10 seconds")
// La goroutine attende il timer2 e
// segnala ogni secondo che passa
go func() {
i := 0
for {
select {
case <-timer2.C:
fmt.Println("go: timer 2 expired")
return
case <-time.After(time.Second):
i++
fmt.Printf("go: still here after %d sec\n", i)
}
}
}()
//Attende 3 secondi prima di fermare il timer
fmt.Println("main: waiting 5 seconds before stopping timer2")
<-time.After(5 * time.Second)
// Ferma timer2
stop2 := timer2.Stop()
// timer2.Stop ritorna un booleano indicante successo
if stop2 {
fmt.Println("main: timer 2 stopped")
}
// Quando un timer è fermato le goroutine bloccate
// sul timer non vengono terminate
// E' possibile risettare un timer
timer2.Reset(3 * time.Second)
fmt.Println("main: timer 2 reset to 3 second")
fmt.Println("main: waiting 5 seconds before exiting")
<-time.After(5 * time.Second)
fmt.Println("main: exiting now")
}
Ad un timer si può applicare la funzione Stop
: il timer continua ad esistere, ma il suo tempo di scatto è ora infinito.
Con la funzione Reset
il timer può essere fatto ripartire con un tempo diverso di scatto.
Tutto il codice che attendeva in lettura sul canale C
del timer si comporta di conseguenza.
Distruzione di timer
Un timer viene automaticamente distrutto dopo lo scatto e quindi il primo codice che lo legge vince.
Questo è dimostrato dal seguente programma:
(321-timer-two.go):
package main
import (
"fmt"
"time"
)
func main() {
tim := time.NewTimer(3 * time.Second)
go func() {
i := 0
for {
select {
case <-tim.C:
fmt.Println("go1: timer expired")
return
case <-time.After(time.Second):
i++
fmt.Printf("go1: still here after %d sec\n", i)
}
}
}()
go func() {
i := 0
for {
select {
case <-tim.C:
fmt.Println("go2: timer expired")
return
case <-time.After(time.Second):
i++
fmt.Printf("go2: still here after %d sec\n", i)
}
}
}()
<-time.After(10 * time.Second)
fmt.Println("main: exiting now")
}
E' aleatorio se sia prima go1 o go2 a ricevere il segnale dal timer.
Evidentemente o non più di una goroutine deve attendere lo stesso timer, oppure abbiamo bisogno di informare le altre goroutines dello scatto avvenuto.