J'essaie de planifier un événement répétitif pour qu'il s'exécute chaque minute dans Python 3.
J'ai vu la classe sched.scheduler
mais je me demande s'il existe un autre moyen de le faire. J'ai entendu dire que je pourrais utiliser plusieurs threads pour cela, ce qui ne me dérangerait pas.
En gros, je demande du JSON, puis je l’analyse; sa valeur change avec le temps.
Pour utiliser sched.scheduler
, je dois créer une boucle pour lui demander de planifier l'exécution de l'événement pour une heure:
scheduler = sched.scheduler(time.time, time.sleep)
# Schedule the event. THIS IS UGLY!
for i in range(60):
scheduler.enter(3600 * i, 1, query_rate_limit, ())
scheduler.run()
Quelles sont les autres façons de faire cela?
Vous pouvez utiliser threading.Timer
, mais cela permet également de planifier un événement unique, de la même manière que la méthode .enter
des objets du planificateur.
Le modèle normal (dans n’importe quel langage) pour transformer un planificateur ponctuel en un planificateur périodique consiste à demander à chaque événement de se planifier à nouveau à l’intervalle spécifié. Par exemple, avec sched
, je n’utiliserais pas une boucle comme vous le faites, mais plutôt quelque chose comme:
def periodic(scheduler, interval, action, actionargs=()):
scheduler.enter(interval, 1, periodic,
(scheduler, interval, action, actionargs))
action(*actionargs)
et initier l'ensemble "calendrier périodique pour toujours" avec un appel
periodic(scheduler, 3600, query_rate_limit)
Ou bien, je pourrais utiliser threading.Timer
au lieu de scheduler.enter
, mais le motif est assez similaire.
Si vous avez besoin d’une variante plus précise (par exemple, arrêtez la reprogrammation périodique à un moment donné ou à certaines conditions), ce n’est pas trop difficile à gérer avec quelques paramètres supplémentaires.
Mon humble prise sur le sujet:
from threading import Timer
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.function = function
self.interval = interval
self.args = args
self.kwargs = kwargs
self.is_running = False
self.start()
def _run(self):
self.is_running = False
self.start()
self.function(*self.args, **self.kwargs)
def start(self):
if not self.is_running:
self._timer = Timer(self.interval, self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
Usage:
from time import sleep
def hello(name):
print "Hello %s!" % name
print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
sleep(5) # your long-running job goes here...
finally:
rt.stop() # better in a try/finally block to make sure the program ends!
Caractéristiques:
start()
et stop()
peuvent appeler plusieurs fois même si le chronomètre a déjà démarré/arrêtéinterval
à tout moment, il sera effectif après la prochaine exécution. Idem pour args
, kwargs
et même function
!Vous pouvez utiliser schedule . Cela fonctionne sur Python 2.7 et 3.3 et est plutôt léger:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
while 1:
schedule.run_pending()
time.sleep(1)
Basé sur la réponse de MestreLion, il résout un petit problème avec le multithreading:
from threading import Timer, Lock
class Periodic(object):
"""
A periodic task running in threading.Timers
"""
def __init__(self, interval, function, *args, **kwargs):
self._lock = Lock()
self._timer = None
self.function = function
self.interval = interval
self.args = args
self.kwargs = kwargs
self._stopped = True
if kwargs.pop('autostart', True):
self.start()
def start(self, from_run=False):
self._lock.acquire()
if from_run or self._stopped:
self._stopped = False
self._timer = Timer(self.interval, self._run)
self._timer.start()
self._lock.release()
def _run(self):
self.start(from_run=True)
self.function(*self.args, **self.kwargs)
def stop(self):
self._lock.acquire()
self._stopped = True
self._timer.cancel()
self._lock.release()
Vous pouvez utiliser le Advanced Python Scheduler . Il a même une interface semblable à celle de cron.
Utilisez Céleri .
from celery.task import PeriodicTask
from datetime import timedelta
class ProcessClicksTask(PeriodicTask):
run_every = timedelta(minutes=30)
def run(self, **kwargs):
#do something
Voici une boucle rapide et peu bloquante avec Thread
:
#!/usr/bin/env python3
import threading,time
def worker():
print(time.time())
time.sleep(5)
t = threading.Thread(target=worker)
t.start()
threads = []
t = threading.Thread(target=worker)
threads.append(t)
t.start()
time.sleep(7)
print("Hello World")
Il n'y a rien de particulièrement spécial, la worker
crée un nouveau thread avec un délai. Peut-être pas le plus efficace, mais assez simple. La réponse de northtree serait la voie à suivre si vous avez besoin d'une solution plus sophistiquée.
Et sur la base de this , nous pouvons faire la même chose, avec Timer
:
#!/usr/bin/env python3
import threading,time
def hello():
t = threading.Timer(10.0, hello)
t.start()
print( "hello, world",time.time() )
t = threading.Timer(10.0, hello)
t.start()
time.sleep(12)
print("Oh,hai",time.time())
time.sleep(4)
print("How's it going?",time.time())
Sur la base de la réponse d'Alex Martelli, j'ai implémenté la version decorator qui est plus facile à intégrer.
import sched
import time
import datetime
from functools import wraps
from threading import Thread
def async(func):
@wraps(func)
def async_func(*args, **kwargs):
func_hl = Thread(target=func, args=args, kwargs=kwargs)
func_hl.start()
return func_hl
return async_func
def schedule(interval):
def decorator(func):
def periodic(scheduler, interval, action, actionargs=()):
scheduler.enter(interval, 1, periodic,
(scheduler, interval, action, actionargs))
action(*actionargs)
@wraps(func)
def wrap(*args, **kwargs):
scheduler = sched.scheduler(time.time, time.sleep)
periodic(scheduler, interval, func)
scheduler.run()
return wrap
return decorator
@async
@schedule(1)
def periodic_event():
print(datetime.datetime.now())
if __== '__main__':
print('start')
periodic_event()
print('end')
Voir mon échantillon
import sched, time
def myTask(m,n):
print n+' '+m
def periodic_queue(interval,func,args=(),priority=1):
s = sched.scheduler(time.time, time.sleep)
periodic_task(s,interval,func,args,priority)
s.run()
def periodic_task(scheduler,interval,func,args,priority):
func(*args)
scheduler.enter(interval,priority,periodic_task,
(scheduler,interval,func,args,priority))
periodic_queue(1,myTask,('world','hello'))