Je veux exécuter de manière répétée une fonction en Python toutes les 60 secondes (comme un NSTimer dans Objective C). Ce code fonctionnera en tant que démon et ressemblera en réalité à l'appel du script python toutes les minutes à l'aide d'un cron, mais sans que cela soit configuré par l'utilisateur.
Dans cette question sur un cron implémenté en Python , la solution semble effectivement juste sleep () pendant x secondes. Je n'ai pas besoin d'une fonctionnalité aussi avancée, alors peut-être qu'un tel système fonctionnerait
while True:
# Code executed here
time.sleep(60)
Y at-il des problèmes prévisibles avec ce code?
Utilisez le module sched , qui implémente un planificateur d'événements à usage général.
import sched, time
s = sched.scheduler(time.time, time.sleep)
def do_something(sc):
print "Doing stuff..."
# do your stuff
s.enter(60, 1, do_something, (sc,))
s.enter(60, 1, do_something, (s,))
s.run()
Verrouillez simplement votre boucle de temps sur l'horloge système. Facile.
import time
starttime=time.time()
while True:
print "tick"
time.sleep(60.0 - ((time.time() - starttime) % 60.0))
Vous voudrez peut-être envisager Twisted qui est une bibliothèque de réseau Python qui implémente le Reactor Pattern .
from twisted.internet import task, reactor
timeout = 60.0 # Sixty seconds
def doWork():
#do work here
pass
l = task.LoopingCall(doWork)
l.start(timeout) # call every sixty seconds
reactor.run()
Twisted implémente probablement déjà de nombreuses fonctionnalités dont vous aurez éventuellement besoin (démonisation, journalisation ou gestion des exceptions comme indiqué par bobince) et constituera probablement une solution plus robuste.
Si vous voulez un moyen non bloquant d'exécuter votre fonction périodiquement, au lieu d'une boucle infinie bloquante, j'utiliserais un minuteur fileté. De cette façon, votre code peut continuer à s'exécuter, effectuer d'autres tâches et toujours appeler votre fonction toutes les n secondes. J'utilise beaucoup cette technique pour imprimer des informations de progression sur des tâches longues, intensives en CPU/disque/réseau.
Voici le code que j'ai posté dans une question similaire, avec les contrôles start () et stop ():
from threading import Timer
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
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
!Le moyen le plus simple je crois d’être:
import time
def executeSomething():
#code here
time.sleep(60)
while True:
executeSomething()
De cette façon, votre code est exécuté, puis il attend 60 secondes, puis il s'exécute à nouveau, attend, exécute, etc ....__ Pas besoin de compliquer les choses.
Voici une mise à jour du code de MestreLion qui évite de dériver dans le temps:
import threading
import time
class RepeatedTimer(object):
def __init__(self, interval, function, *args, **kwargs):
self._timer = None
self.interval = interval
self.function = function
self.args = args
self.kwargs = kwargs
self.is_running = False
self.next_call = time.time()
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.next_call += self.interval
self._timer = threading.Timer(self.next_call - time.time(), self._run)
self._timer.start()
self.is_running = True
def stop(self):
self._timer.cancel()
self.is_running = False
import time, traceback
def every(delay, task):
next_time = time.time() + delay
while True:
time.sleep(max(0, next_time - time.time()))
try:
task()
except Exception:
traceback.print_exc()
# in production code you might want to have this instead of course:
# logger.exception("Problem while executing repetitive task.")
# skip tasks if we are behind schedule:
next_time += (time.time() - next_time) // delay * delay + delay
def foo():
print("foo", time.time())
every(5, foo)
Si vous voulez faire cela sans bloquer votre code restant, vous pouvez l'utiliser pour le laisser s'exécuter dans son propre thread:
import threading
threading.Thread(target=lambda: every(5, foo)).start()
Cette solution combine plusieurs fonctionnalités rarement combinées dans les autres solutions:
threading.Timer
ou autre), cela mettra fin à la chaîne . Aucune autre exécution ne se produira alors, même si la raison du problème est déjà corrigée. Une simple boucle et attendre avec une simple sleep()
est beaucoup plus robuste en comparaison.next_time += delay
à la place.J'ai rencontré un problème similaire il y a quelque temps. Peut-être http://cronus.readthedocs.org pourrait aider?
Pour la version 0.2, l'extrait suivant fonctionne
import cronus.beat as beat
beat.set_rate(2) # 2 Hz
while beat.true():
# do some time consuming work here
beat.sleep() # total loop duration would be 0.5 sec
La principale différence entre cela et cron est qu'une exception tue définitivement le démon. Vous voudrez peut-être envelopper avec un capteur d'exception et un enregistreur.
Une réponse possible:
import time
t=time.time()
while True:
if time.time()-t>10:
#run your task here
t=time.time()
par exemple, afficher l'heure locale actuelle
import datetime
import glib
import logger
def get_local_time():
current_time = datetime.datetime.now().strftime("%H:%M")
logger.info("get_local_time(): %s",current_time)
return str(current_time)
def display_local_time():
logger.info("Current time is: %s", get_local_time())
return True
# call every minute
glib.timeout_add(60*1000, display_local_time)
''' tracking number of times it prints'''
import threading
global timeInterval
count=0
def printit():
threading.Timer(timeInterval, printit).start()
print( "Hello, World!")
global count
count=count+1
print(count)
printit
if __== "__main__":
timeInterval= int(input('Enter Time in Seconds:'))
printit()
J'utilise la méthode after () de Tkinter, qui ne "vole pas le jeu" (comme le module sched présenté précédemment), c'est-à-dire qu'elle permet à d'autres opérations de s'exécuter en parallèle:
import Tkinter
def do_something1():
global n1
n1 += 1
if n1 == 6: # (Optional condition)
print "* do_something1() is done *"; return
# Do your stuff here
# ...
print "do_something1() "+str(n1)
tk.after(1000, do_something1)
def do_something2():
global n2
n2 += 1
if n2 == 6: # (Optional condition)
print "* do_something2() is done *"; return
# Do your stuff here
# ...
print "do_something2() "+str(n2)
tk.after(500, do_something2)
tk = Tkinter.Tk();
n1 = 0; n2 = 0
do_something1()
do_something2()
tk.mainloop()
do_something1()
et do_something2()
peuvent fonctionner en parallèle et quelle que soit la vitesse d'intervalle. Ici, le deuxième sera exécuté deux fois plus vite. Notez également que j’ai utilisé un simple compteur comme condition pour terminer l’une ou l’autre des fonctions. Vous pouvez utiliser n’importe quelle autre concurrence ou aucune autre si vous souhaitez exécuter une fonction jusqu’à ce que le programme se termine (par exemple, une horloge).
J'utilise ceci pour provoquer 60 événements par heure, la plupart des événements se produisant au même nombre de secondes après la minute entière:
import math
import time
import random
TICK = 60 # one minute tick size
TICK_TIMING = 59 # execute on 59th second of the tick
TICK_MINIMUM = 30 # minimum catch up tick size when lagging
def set_timing():
now = time.time()
elapsed = now - info['begin']
minutes = math.floor(elapsed/TICK)
tick_elapsed = now - info['completion_time']
if (info['tick']+1) > minutes:
wait = max(0,(TICK_TIMING-(time.time() % TICK)))
print ('standard wait: %.2f' % wait)
time.sleep(wait)
Elif tick_elapsed < TICK_MINIMUM:
wait = TICK_MINIMUM-tick_elapsed
print ('minimum wait: %.2f' % wait)
time.sleep(wait)
else:
print ('skip set_timing(); no wait')
drift = ((time.time() - info['begin']) - info['tick']*TICK -
TICK_TIMING + info['begin']%TICK)
print ('drift: %.6f' % drift)
info['tick'] = 0
info['begin'] = time.time()
info['completion_time'] = info['begin'] - TICK
while 1:
set_timing()
print('hello world')
#random real world event
time.sleep(random.random()*TICK_MINIMUM)
info['tick'] += 1
info['completion_time'] = time.time()
En fonction des conditions réelles, vous pouvez obtenir des ticks de longueur:
60,60,62,58,60,60,120,30,30,60,60,60,60,60...etc.
mais au bout de 60 minutes, vous aurez 60 ticks; et la plupart d'entre eux se produiront au décalage correct à la minute que vous préférez.
Sur mon système, la dérive typique est inférieure à 1/20ème de seconde jusqu'à ce que des corrections soient nécessaires.
L'avantage de cette méthode est la résolution de la dérive d'horloge; Cela peut entraîner des problèmes si vous ajoutez par exemple un élément par tick et que vous attendez 60 éléments ajoutés par heure. Si vous ne tenez pas compte de la dérive, des indications secondaires, telles que les moyennes mobiles, peuvent prendre en compte des données trop anciennes, ce qui entraîne une sortie erronée.
Voici une version adaptée au code de MestreLion . En plus de la fonction originale, ce code:
1) add first_interval utilisé pour déclencher le chronomètre à une heure précise (l'appelant doit calculer le premier_intervalle et le transmettre)
2) résoudre une condition de concurrence dans le code d'origine. Dans le code d'origine, si le thread de contrôle ne parvenait pas à annuler le temporisateur en cours d'exécution ("Arrêtez le temporisateur et annulez l'exécution de l'action du temporisateur. Ceci ne fonctionnera que si le temporisateur est toujours en attente.", Extrait de https: //docs.python.org/2/library/threading.html ), le temporisateur fonctionnera indéfiniment.
class RepeatedTimer(object):
def __init__(self, first_interval, interval, func, *args, **kwargs):
self.timer = None
self.first_interval = first_interval
self.interval = interval
self.func = func
self.args = args
self.kwargs = kwargs
self.running = False
self.is_started = False
def first_start(self):
try:
# no race-condition here because only control thread will call this method
# if already started will not start again
if not self.is_started:
self.is_started = True
self.timer = Timer(self.first_interval, self.run)
self.running = True
self.timer.start()
except Exception as e:
log_print(syslog.LOG_ERR, "timer first_start failed %s %s"%(e.message, traceback.format_exc()))
raise
def run(self):
# if not stopped start again
if self.running:
self.timer = Timer(self.interval, self.run)
self.timer.start()
self.func(*self.args, **self.kwargs)
def stop(self):
# cancel current timer in case failed it's still OK
# if already stopped doesn't matter to stop again
if self.timer:
self.timer.cancel()
self.running = False