Scopo | Schedulatore di eventi per uso generale |
Versione Python | 1.4 |
A partire dal 1 gennaio 2021 le versioni 2.x di Python non sono piu' supportate. Ti invito a consultare la corrispondente versione 3.x dell'articolo per il modulo sched
Il modulo sched implementa uno schedulatore di eventi per eseguire dei compiti ad un tempo specificato. La classe scheduler usa una funzione time per acquisire l'ora attuale, ed una funzione delay per attendere uno specifico periodo di tempo. Le reali unità di tempo non sono importanti, cosa che rende l'interfaccia sufficientemente flessibile per essere usata per diversi scopi.
La funzione
time
viene chiamata senza parametri, e dovrebbe restituire un numero che corrisponde all'ora attuale. La funzione
delay
viene chiamata con un singolo intero come parametro, usando la stessa scala della funzione time, e dovrebbe attendere le unità temporali rappresentate da quel parametro prima di ritornare. Ad esempio le funzioni
time.time()
e
time.sleep()
soddisfano questi requisiti.
Per supportare le applicazioni multi-threaded, la funzione delay viene chiamata con parametro 0 dopo che ogni evento viene generato, per assicurare che gli altri thread abbiano anch'essi la possibilità di venire eseguiti.
Gli eventi possono essere schedulati per essere eseguiti dopo un differimento, oppure ad una certa ora. Per schedularli con differimento, si usa il metodo
enter()
, che chiede 4 parametri:
Questo esempio schedula due eventi diversi da eseguire rispettivamente dopo 2 e 3 secondi. Quando è l'ora di eseguire l'evento, viene chiamata
print_event()
che stampa l'ora attuale ed il parametro name passato all'evento
import sched
import time
scheduler = sched.scheduler(time.time, time.sleep)
def print_event(name):
print 'EVENTO:', time.time(), name
print 'INIZIO:', time.time()
scheduler.enter(2, 1, print_event, ('primo',))
scheduler.enter(3, 1, print_event, ('secondo',))
scheduler.run()
L'output è qualcosa di simile a:
$ python sched_basic.py INIZIO: 1253463104.94 EVENTO: 1253463106.94 primo EVENTO: 1253463107.94 secondo
L'orario stampato per il primo evento è due secondi dopo la partenza, e l'orario del secondo evento è 3 secondi dopo la partenza.
La chiamata a
run()
blocca fino a che tutti gli eventi sono stati elaborati. Ogni evento viene eseguito nello stesso thread, in modo che se un evento necessita di un tempo di esecuzione maggiore rispetto a quello del differimento tra gli eventi, essi saranno sovrapposti. La sovrapposizione viene risolta posponendo l'evento che è più avanti nel tempo. Nessun evento viene perso, ma alcuni eventi potrebbero essere chiamati più tardi del previsto. Nel prossimo esempio
long_event()
contiene una istruzione di
sleep
ma il ritardo potrebbe essere facilmente determinato da un un lungo calcolo o da un blocco su di una operazione di I/O.
import sched
import time
scheduler = sched.scheduler(time.time, time.sleep)
def long_event(name):
print 'INIZIO EVENTO :', time.time(), name
time.sleep(2)
print 'FINE EVENTO :', time.time(), name
print 'PARTENZA:', time.time()
scheduler.enter(2, 1, long_event, ('primo',))
scheduler.enter(3, 1, long_event, ('secondo',))
scheduler.run()
Il risultato è che il secondo evento viene eseguito immediatamente dopo che finisce il primo, visto che per il primo evento ci è voluto abbastanza tempo per spingere l'orario dopo l'orario di partenza desiderato per il secondo evento.
$ python sched_overlap.py PARTENZA: 1255906381.71 INIZIO EVENTO : 1255906383.71 primo FINE EVENTO: 1255906385.71 primo INIZIO EVENTO : 1255906385.71 secondo FINE EVENTO: 1255906387.71 secondo
Se più eventi vengono schedulati nello stesso tempo, i loro valori di priorità vengono usati per determinare l'ordine nel quale essi saranno eseguiti
import sched
import time
scheduler = sched.scheduler(time.time, time.sleep)
def print_event(name):
print 'EVENTO:', time.time(), name
now = time.time()
print 'PARTENZA:', now
scheduler.enterabs(now+2, 2, print_event, ('primo',))
scheduler.enterabs(now+2, 1, print_event, ('secondo',))
scheduler.run()
Per essere sicuri che gli eventi siano schedulati allo stesso esatto orario, viene usato il metodo
enterabs()
in luogo di
enter()
. Il primo parametro per
enterabs()
è l'orario in cui eseguire l'evento, invece che il periodo di tempo di differimento.
$ python sched_priority.py INIZIO: 1253463114.08 EVENTO: 1253463116.08 secondo EVENTO: 1253463116.08 primo
Sia
enter()
che
enterabs()
restituiscono un riferimento all'evento che può essere usato per cancellarlo successivamente. Visto il blocco di
run()
, l'evento deve essere eliminato in un thread differente. Per questo esempio, un thread viene lanciato per eseguire lo scheduler, quindi il thread di esecuzione principale viene usato per cancellare l'evento.
import sched
import threading
import time
scheduler = sched.scheduler(time.time, time.sleep)
# Imposta un valore globale perchè sia modificabile dai thread
counter = 0
def increment_counter(name):
global counter
print 'EVENTO:', time.time(), name
counter += 1
print 'ADESSO:', counter
print 'PARTENZA:', time.time()
e1 = scheduler.enter(2, 1, increment_counter, ('E1',))
e2 = scheduler.enter(3, 1, increment_counter, ('E2',))
# Inizia un thread per eseguire gli eventi
t = threading.Thread(target=scheduler.run)
t.start()
# Ritorna al thread principale, elimina il primo evento schedulato
scheduler.cancel(e1)
# Attende che lo the scheduler finisca l'esecuzione nel thread
t.join()
print 'FINALE:', counter
Sono stati schedulati due eventi, ma il primo successivamente è stato eliminato. Solo il secondo evento è stato eseguito, quindi la variabile counter viene incrementata una sola volta.
$ python sched_cancel.py PARTENZA: 1253463116.24 EVENTO: 1253463119.24 E2 ADESSO: 1 FINALE: 1