Le langage Go propose des outils natifs puissants au sein de son package time pour gérer l'exécution de code de manière différée ou périodique. Pour des besoins plus complexes, des bibliothèques tierces permettent d'implémenter une syntaxe proche de l'ordonnanceur Cron d'Unix.
Utiilsation des Timers avec le package standard
L'objet Timer est conçu pour une exécution unique après un délai spécifié. Il communique avec le programme via un canal (channel).
Gestion basique avec NewTimer
La fonction time.NewTimer permet de déclencher un événement une seule fois. On peut arrêter le timer avant son expiration ou réinitialiser son décompte.
package main
import (
"fmt"
"time"
)
func exempleTimer() {
// Création d'un timer de 2 secondes
delai := 2 * time.Second
monTimer := time.NewTimer(delai)
fmt.Printf("Début du processus : %s\n", time.Now().Format("15:04:05"))
// Blocage jusqu'à la réception du signal sur le canal C
<-monTimer.C
fmt.Printf("Exécution après délai : %s\n", time.Now().Format("15:04:05"))
// Réinitialisation pour une nouvelle utilisation
monTimer.Reset(1 * time.Second)
<-monTimer.C
fmt.Printf("Exécution après réinitialisation : %s\n", time.Now().Format("15:04:05"))
// Exemple d'arrêt manuel
stopTimer := time.NewTimer(5 * time.Second)
go func() {
reussi := stopTimer.Stop()
if reussi {
fmt.Println("Le timer a été arrêté avec succès avant expiration.")
}
}()
}
Alternatives : After et AfterFunc
Pour des cas plus simples, Go propose des fonctions utilitaires qui évitent de manipuler directement l'objet Timer.
func methodesAlternatives() {
// time.After renvoie directement le canal
fmt.Println("Attente de 1 seconde...")
<-time.After(time.Second)
fmt.Println("Terminé.")
// time.AfterFunc exécute une fonction dans sa propre goroutine après le délai
signalFin := make(chan bool)
time.AfterFunc(2*time.Second, func() {
fmt.Println("Tâche asynchrone exécutée")
signalFin <- true
})
<-signalFin
}
Exécution périodique avec Ticker
Contrairement au Timer, le Ticker envoie des signaux sur son canal à intervalles réguliers. C'est l'outil idéal pour les tâches de fond répétitives.
package main
import (
"fmt"
"time"
)
type GestionnaireTache struct {
frequence *time.Ticker
quitter chan bool
}
func nouveauGestionnaire(secondes int) *GestionnaireTache {
return &GestionnaireTache{
frequence: time.NewTicker(time.Duration(secondes) * time.Second),
quitter: make(chan bool),
}
}
func (gt *GestionnaireTache) Demarrer() {
go func() {
for {
select {
case t := <-gt.frequence.C:
fmt.Printf("Action effectuée à %v\n", t.Format("15:04:05"))
case <-gt.quitter:
gt.frequence.Stop()
return
}
}
}()
}
func exempleTicker() {
worker := nouveauGestionnaire(1)
worker.Demarrer()
// Laisser tourner 3 secondes avant d'arrêter
time.Sleep(3 * time.Second)
worker.quitter <- true
fmt.Println("Ticker stoppé.")
}
Ordonnencement avancé avec la bibliothèque Cron
Pour gérer des calendriers complexes (ex: "tous les lundis à 3h du matin"), la bibliothèque robfig/cron est la référence en Go.
Installation
go get github.com/robfig/cron/v3
Mise en œuvre
Cette bibliothèque permet de définir des tâches en utilisant la syntaxe standard de Cron ou une syntaxe étendue incluant les secondes.
package main
import (
"fmt"
"github.com/robfig/cron/v3"
"time"
)
func planificationAvancee() {
// Initialisation avec support des secondes et fuseau horaire spécifique
paris, _ := time.LoadLocation("Europe/Paris")
c := cron.New(cron.WithSeconds(), cron.WithLocation(paris))
// Expression : Secondes | Minutes | Heures | Jour du mois | Mois | Jour de la semaine
spec := "*/5 * * * * *" // Toutes les 5 secondes
id1, _ := c.AddFunc(spec, func() {
fmt.Printf("[Tâche 1] Exécution à %s\n", time.Now().Format("15:04:05"))
})
c.AddFunc("@hourly", func() {
fmt.Println("Cette tâche s'exécute chaque heure.")
})
c.Start()
// Inspection des tâches en cours
for _, entree := range c.Entries() {
fmt.Printf("Job ID: %v, Prochaine exécution: %v\n", entree.ID, entree.Next)
}
// Simulation d'activité
time.Sleep(16 * time.Second)
// Suppression d'une tâche spécifique
c.Remove(id1)
c.Stop() // Arrête l'ordonnanceur
}
Le package cron gère nativement les goroutines : chaque tâche planifiée est lancée dans son propre thread léger, évitant ainsi que l'exécution d'une tâche ne bloque l'ordonnanceur global.