Ejecutar acciones periódicas [duplicado]
Estoy trabajando en Windows. Quiero ejecutar una función foo()
cada 10 segundos.
¿Cómo hago esto?
Al final de foo()
, cree un Timer
que se llame foo()
a sí mismo después de 10 segundos.
Porque, Timer
crea un nuevo thread
para llamar foo()
.
Puedes hacer otras cosas sin que te bloqueen.
import time, threading
def foo():
print(time.ctime())
threading.Timer(10, foo).start()
foo()
#output:
#Thu Dec 22 14:46:08 2011
#Thu Dec 22 14:46:18 2011
#Thu Dec 22 14:46:28 2011
#Thu Dec 22 14:46:38 2011
Simplemente dormir durante 10 segundos o usarlo threading.Timer(10,foo)
provocará una desviación del tiempo de inicio. (Es posible que esto no le importe, o puede ser una fuente importante de problemas dependiendo de su situación exacta). Puede haber dos causas para esto: imprecisiones en el tiempo de activación de su subproceso o el tiempo de ejecución de su función.
Puedes ver algunos resultados al final de esta publicación, pero primero un ejemplo de cómo solucionarlo. Debe realizar un seguimiento de cuándo se debe llamar a continuación a su función en lugar de cuándo se llamó realmente y tener en cuenta la diferencia.
Aquí hay una versión que se desvía ligeramente:
import datetime, threading
def foo():
print datetime.datetime.now()
threading.Timer(1, foo).start()
foo()
Su salida se ve así:
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
Puede ver que el conteo de subsegundos aumenta constantemente y, por lo tanto, el tiempo de inicio está "a la deriva".
Este es el código que explica correctamente la deriva:
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()
Su salida se ve así:
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
Aquí puede ver que ya no hay ningún aumento en los tiempos inferiores al segundo.
Si sus eventos ocurren con mucha frecuencia, es posible que desee ejecutar el temporizador en un solo hilo, en lugar de iniciar un nuevo hilo para cada evento. Teniendo en cuenta la deriva, esto se vería así:
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()
Sin embargo, su aplicación no saldrá normalmente, deberá finalizar el hilo del temporizador. Si desea salir normalmente cuando su aplicación haya terminado, sin cerrar manualmente el hilo, debe usar
timerThread = threading.Thread(target=foo)
timerThread.daemon = True
timerThread.start()
Me sorprende no encontrar una solución utilizando un generador para cronometrar. Acabo de diseñar este para mis propios fines.
Esta solución: subproceso único, sin creación de instancias de objetos en cada período, utiliza un generador de tiempos, sólido como una roca en el tiempo hasta la precisión del time
módulo (a diferencia de varias de las soluciones que he probado desde el intercambio de pila).
Nota: para Python 2.x, reemplace next(g)
a continuación con g.next()
.
import time
def do_every(period,f,*args):
def g_tick():
t = time.time()
while True:
t += period
yield max(t - time.time(),0)
g = g_tick()
while True:
time.sleep(next(g))
f(*args)
def hello(s):
print('hello {} ({:.4f})'.format(s,time.time()))
time.sleep(.3)
do_every(1,hello,'foo')
Resultados en, por ejemplo:
hello foo (1421705487.5811)
hello foo (1421705488.5811)
hello foo (1421705489.5809)
hello foo (1421705490.5830)
hello foo (1421705491.5803)
hello foo (1421705492.5808)
hello foo (1421705493.5811)
hello foo (1421705494.5811)
hello foo (1421705495.5810)
hello foo (1421705496.5811)
hello foo (1421705497.5810)
hello foo (1421705498.5810)
hello foo (1421705499.5809)
hello foo (1421705500.5811)
hello foo (1421705501.5811)
hello foo (1421705502.5811)
hello foo (1421705503.5810)
Tenga en cuenta que este ejemplo incluye una simulación de la CPU haciendo otra cosa durante 0,3 segundos cada período. Si lo cambiaras para que fuera aleatorio cada vez, no importaría. El máximo en la yield
línea sirve para proteger sleep
contra números negativos en caso de que la función que se llama demore más que el período especificado. En ese caso, se ejecutaría inmediatamente y recuperaría el tiempo perdido en el momento de la siguiente ejecución.
Quizás el módulo programado satisfaga sus necesidades.
Alternativamente, considere usar un objeto Timer .