Planification et exécution de tâches différées en Go

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.

Étiquettes: golang cron timer Concurrency backend

Publié le 13 juin à 00h34