Exécuter des actions périodiques en Python



Answers

Il suffit de dormir pendant 10 secondes ou d'utiliser le threading.Timer(10,foo) entraîne une dérive du temps de démarrage. (Cela peut ne pas vous intéresser, ou cela peut être une source importante de problèmes en fonction de votre situation exacte.) Il peut y avoir deux raisons à cela: inexactitudes dans le temps de réveil de votre thread ou temps d'exécution de votre fonction.

Vous pouvez voir quelques résultats à la fin de ce post, mais d'abord un exemple de la façon de le réparer. Vous devez suivre quand votre fonction doit être appelée par opposition à quand elle a été appelée et tenir compte de la différence.

Voici une version qui dérive légèrement:

import datetime, threading

def foo():
    print datetime.datetime.now()
    threading.Timer(1, foo).start()

foo()

Sa sortie ressemble à ceci:

2013-08-12 13:05:36.483580
2013-08-12 13:05:37.484931
2013-08-12 13:05:38.485505
2013-08-12 13:05:39.486945
2013-08-12 13:05:40.488386
2013-08-12 13:05:41.489819
2013-08-12 13:05:42.491202
2013-08-12 13:05:43.492486
2013-08-12 13:05:44.493865
2013-08-12 13:05:45.494987
2013-08-12 13:05:46.496479
2013-08-12 13:05:47.497824
2013-08-12 13:05:48.499286
2013-08-12 13:05:49.500232

Vous pouvez voir que le nombre de sous-secondes augmente constamment et que, par conséquent, l'heure de début est "dérivante".

C'est un code qui prend correctement en compte la dérive:

import datetime, threading, time

next_call = time.time()

def foo():
  global next_call
  print datetime.datetime.now()
  next_call = next_call+1
  threading.Timer( next_call - time.time(), foo ).start()

foo()

Sa sortie ressemble à ceci:

2013-08-12 13:21:45.292565
2013-08-12 13:21:47.293000
2013-08-12 13:21:48.293939
2013-08-12 13:21:49.293327
2013-08-12 13:21:50.293883
2013-08-12 13:21:51.293070
2013-08-12 13:21:52.293393

Ici, vous pouvez voir qu'il n'y a plus d'augmentation de la sous-seconde fois.

Si vos événements se produisent très fréquemment, vous pouvez exécuter le minuteur dans un seul thread, plutôt que de démarrer un nouveau thread pour chaque événement. Tout en tenant compte de la dérive, cela ressemblerait à:

import datetime, threading, time

def foo():
    next_call = time.time()
    while True:
        print datetime.datetime.now()
        next_call = next_call+1;
        time.sleep(next_call - time.time())

timerThread = threading.Thread(target=foo)
timerThread.start()

Cependant, votre application ne sortira pas normalement, vous devrez tuer le thread minuteur. Si vous voulez quitter normalement lorsque votre application est terminée, sans supprimer manuellement le thread, vous devez utiliser

timerThread = threading.Thread(target=foo)
timerThread.daemon = True
timerThread.start()
Question

Je travaille sur Windows. Je veux exécuter une fonction foo () toutes les 10 secondes.

Comment puis-je faire cela?




Voici une version basée sur le sommeil simple thread simple qui dérive, mais essaie de corriger automatiquement quand il détecte la dérive.

NOTE: Cela ne fonctionnera que si les 3 hypothèses raisonnables suivantes sont satisfaites:

  1. La période de temps est beaucoup plus grande que le temps d'exécution de la fonction en cours d'exécution
  2. La fonction en cours d'exécution prend approximativement le même temps à chaque appel
  3. La quantité de dérive entre les appels est inférieure à une seconde

-

from datetime import timedelta
from datetime import datetime

def exec_every_n_seconds(n,f):
    first_called=datetime.now()
    f()
    num_calls=1
    drift=timedelta()
    time_period=timedelta(seconds=n)
    while 1:
        time.sleep(n-drift.microseconds/1000000.0)
        current_time = datetime.now()
        f()
        num_calls += 1
        difference = current_time - first_called
        drift = difference - time_period* num_calls
        print "drift=",drift



Cela insère une pause de 10 secondes entre chaque appel à foo() , ce qui correspond approximativement à ce que vous avez demandé lorsque l'appel se termine rapidement.

import time
while True:
  foo()
  time.sleep(10)

edit: Faire d'autres choses pendant que votre foo() est appelée dans un thread d'arrière-plan

import time
import sys
import threading

def foo():
  sys.stdout.write('({}) foo\n'.format(time.ctime()))

def foo_target():
  while True:
    foo()
    time.sleep(10)

t = threading.Thread(target=foo_target)
t.daemon = True
t.start()
raw_input('do other things...')



Peut-être que le module sched répondra à vos besoins.

Vous pouvez également envisager d'utiliser un objet Timer .




Links



Tags

python python