2010-02-08 11 views
50

Je veux exécuter une fonction toutes les 60 secondes sur Python mais je ne veux pas être bloqué entre-temps.Comment exécuter une fonction de manière asynchrone toutes les 60 secondes en Python?

Comment puis-je le faire de manière asynchrone?

import threading 
import time 

def f(): 
    print("hello world") 
    threading.Timer(3, f).start() 

if __name__ == '__main__': 
    f()  
    time.sleep(20) 

Avec ce code, la fonction f est exécutée toutes les 3 secondes dans le délai de 20 secondes. À la fin, il donne une erreur et je pense que c'est parce que le threading.timer n'a pas été annulé.

Comment l'annuler?

Merci d'avance!

+0

Votre programme se termine après la création du premier minuteur. –

+0

Afin d'annuler le Timer, vous devez garder une référence quelque part, et appeler sa méthode 'cancel'. –

+0

Notez que toutes les solutions publiées à ce jour souffrent d'une (accumulation) cumulative d'erreurs. C'est-à-dire, plutôt que de calculer la durée de sommeil suivante en fonction d'un temps de départ fixe, ils dorment simplement pendant «60 autres». Cela ne dormira jamais pendant moins de 60 ans, et dormira généralement pendant un peu plus de 60 ans. Ce n'est pas toujours un problème, mais si vous voulez dormir exactement 1440 fois par jour, assurez-vous de compenser cette erreur. –

Répondre

87

Vous pouvez essayer la classe threading.Timer: http://docs.python.org/library/threading.html#timer-objects.

import threading 

def f(f_stop): 
    # do something here ... 
    if not f_stop.is_set(): 
     # call f() again in 60 seconds 
     threading.Timer(60, f, [f_stop]).start() 

f_stop = threading.Event() 
# start calling f now and every 60 sec thereafter 
f(f_stop) 

# stop the thread when needed 
#f_stop.set() 
+0

avec ce code il exécute seulement une fois, est-ce qu'il manque quelque chose pour exécuter plus de fois? –

+3

Vous devez '.start()' le minuteur, je l'ai édité. –

+0

Oups, merci d'ajouter le .start() ... J'aurais dû copier/coller depuis mon interpréteur :). Maintenant, il devrait vraiment être appelé toutes les 60 secondes plutôt qu'une seule fois. –

0

Pourquoi ne pas créer un thread dédié, dans lequel vous mettez une boucle simple de couchage:

#!/usr/bin/env python 
import time 
while True: 
    # Your code here 
    time.sleep(60) 
+7

Ce n'est pas Python. –

+1

Non, mais c'est l'idée. Quand j'ai répondu, il n'y avait pas de code, donc c'était juste une idée. – Aif

+0

@MikeGraham Pourriez-vous expliquer pourquoi ce n'est pas Python? – Dunatotatos

2

La méthode la plus simple consiste à créer un thread d'arrière-plan qui exécute quelque chose toutes les 60 secondes. Une implémentation triviale est:

class BackgroundTimer(Thread): 
    def run(self): 
     while 1: 
     Time.sleep(60) 
     # do something 


# ... SNIP ... 
# Inside your main thread 
# ... SNIP ... 

timer = BackgroundTimer() 
timer.start() 

De toute évidence, cela si le « faire quelque chose » prend beaucoup de temps, vous devrez tenir compte pour cela dans votre déclaration de sommeil. Mais cela sert de bonne approximation.

+0

J'espère que vous réalisez que la méthode "__init__" vide est superflue :) –

+0

Bien sûr ... corrigé :-) Merci. – 0xfe

+0

La façon d'être indépendant du temps d'exécution est d'utiliser une horloge en temps réel (par exemple, 'time.time()' ou 'datetime.now() '), vérifiez l'heure au début de l'exécution de la tâche, ajoutez 60 secondes pour obtenir le temps du prochain tir, puis vérifiez à nouveau à la fin et soustrayez le temps de tir suivant pour obtenir votre temps de sommeil. –

2

Cela dépend de ce que vous voulez réellement faire en attendant. Les fils sont la manière la plus générale et la moins préférée de le faire; vous devez être conscient des problèmes de threading lorsque vous l'utilisez: tout le code (non-Python) ne permet pas l'accès à partir de plusieurs threads simultanément, la communication entre threads doit être faite en utilisant des structures de données thread-safe comme Queue.Queue, vous ne pourrez pas interrompre le fil de l'extérieur, et terminer le programme pendant que le fil est encore en cours peut conduire à un interprète bloqué ou à des traçages parasites.

Souvent, il existe un moyen plus simple. Si vous faites ceci dans un programme d'interface graphique, employez la fonctionnalité de temporisateur ou d'événement de bibliothèque de GUI. Toutes les interfaces graphiques ont ceci. De même, si vous utilisez un autre système d'événements, comme Twisted ou un autre modèle de processus serveur, vous devriez pouvoir vous connecter à la boucle d'événements principale pour l'appeler régulièrement. Les approches sans threading provoquent le blocage de votre programme lorsque la fonction est en attente, mais pas entre les appels de fonction.

+0

Le piège de faire cela sur un temporisateur GUI est que l'interface graphique est gelée pendant que vous gérez le délai d'expiration. Si la tâche est courte, pas de problème. Sinon, votre programme semble se bloquer toutes les minutes. –

2

J'ai googlé et trouvé le cadre Python circuits, ce qui permet d'attendre
pour un événement particulier.

La méthode .callEvent(self, event, *channels) des circuits contient un incendie et suspendez-jusqu'à-réponse des fonctionnalités, la documentation dit:

Feu l'événement donné aux canaux spécifiés et suspendre l'exécution jusqu'à ce qu'il ait été expédié. Cette méthode peut seulement être invoquée comme argument à yield au niveau d'exécution supérieur d'un gestionnaire (par exemple "yield self.callEvent(event)"). Il crée et renvoie effectivement un générateur qui sera appelé par la boucle principale jusqu'à ce que l'événement ait été envoyé (voir: func: circuits.core.handlers.handler).

Je vous souhaite aussi utile que je :)
./regards

+0

Bien que hors sujet légèrement. C'est un excellent lien. – Glycerine

1

Si vous voulez appeler la méthode « sur l'horloge » (par exemple toutes les heures à l'heure), vous pouvez intégrer l'idée suivante avec mécanisme selon thread choisi:

import time 

def wait(n): 
    '''Wait until the next increment of n seconds''' 
    x = time.time() 
    time.sleep(n-(x%n)) 
    print time.asctime() 
1

Je pense que la bonne façon de lancer un fil est à plusieurs reprises la prochaine:

import threading 
import time 

def f(): 
    print("hello world") # your code here 
    myThread.run() 

if __name__ == '__main__': 
    myThread = threading.Timer(3, f) # timer is set to 3 seconds 
    myThread.start() 
    time.sleep(10) # it can be loop or other time consuming code here 
    if myThread.is_alive(): 
     myThread.cancel() 

Avec ce code, la fonction f est exécutée toutes les 3 secondes dans les 10 secondes suivantes. Sleep (10). À la fin de la course du fil est annulé.

Questions connexes