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 Cdel 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.