2010-03-08 11 views
22

sto cercando di pianificare un evento ricorrente per l'esecuzione ogni minuto in Python 3.pianificato un evento ricorrente in Python 3

Ho visto classe sched.scheduler ma mi chiedo se c'è un altro modo per farlo. Ho sentito che potrei usare più thread per questo, cosa che non mi dispiacerebbe fare.

Praticamente sto richiedendo alcuni JSON e quindi analizzandolo; il suo valore cambia nel tempo.

Per utilizzare sched.scheduler devo creare un ciclo farne richiesta per pianificare l'ancora a correre per un'ora: c'è

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() 

Quali altri modi per farlo sono?

+0

duplicati di tutto della questione "tabella di marcia" per Python 2. Tutti questi: http://stackoverflow.com/search?q=%5Bpython%5D+schedule –

+0

duplicati: http: // StackOverflow. it/questions/373335/suggestions-for-a-cron-like-scheduler-in-python –

risposta

33

È possibile utilizzare threading.Timer, ma questo pianifica anche un evento una tantum, analogamente al metodo .enter degli oggetti dello scheduler.

Lo schema normale (in qualsiasi lingua) per trasformare uno scheduler una tantum in uno schedulatore periodico consiste nel fare in modo che ogni evento si ripeta automaticamente all'intervallo specificato. Ad esempio, con sched, non vorrei usare un ciclo come si sta facendo, ma piuttosto qualcosa di simile:

def periodic(scheduler, interval, action, actionargs=()): 
    scheduler.enter(interval, 1, periodic, 
        (scheduler, interval, action, actionargs)) 
    action(*actionargs) 

e di avviare l'intero "calendario per sempre periodica" con un invito

periodic(scheduler, 3600, query_rate_limit) 

Or , Potrei usare threading.Timer invece di scheduler.enter, ma il modello è abbastanza simile.

Se è necessaria una variazione più raffinata (ad esempio, interrompere la riprogrammazione periodica in un dato momento o in determinate condizioni), non è troppo difficile da gestire con alcuni parametri aggiuntivi.

+4

Bene, in java ho timer.scheduleAtFixedRate() E vero multithreading. E tutti dicono che in python scriviamo meno codice ... Um-hum ... Solo dicendo ... – user1685095

+2

@ user1685095 ci sono * sempre * eccezioni a qualsiasi affermazione generalizzata come quella, purtroppo. – Ponkadoodle

+1

@Wallacoloo vuol dire che * non * * sempre eccezioni? :) –

17

Il mio modesto introito sul tema:

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 

utilizzo:

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! 

Caratteristiche:

    solo
  • libreria standard, senza dipendenze esterne
  • Utilizza lo schema suggerito da Alex Martnelli
  • start() e stop() sono sicuri da chiamare più volte, anche se il timer è già iniziato/fermato
  • funzione da chiamare può avere argomenti posizionali e denominati
  • È possibile modificare interval in qualsiasi momento, sarà efficace dopo la successiva esecuzione. Lo stesso vale per args, kwargs e anche per function!
+0

Bella classe, ma ha un piccolo problema se start() viene eseguito in un ciclo. Può passare il controllo is_running a causa della funzione _run eseguita in un altro thread. Quindi l'ultimo self._timer viene riassegnato e non può essere fermato. Controlla la mia risposta per la versione corretta. – fdb

+0

@fdb: Non sono sicuro di aver capito i tuoi punti. Se esegui 'start()' in un ciclo usando l'istanza della * stessa * classe, non farà nulla. Se crei un'istanza * nuova *, attiverà un timer diverso (che consente di avere più timer simultanei). Per quanto riguarda il multithreading, sì, è escluso che ogni 'start()' (o '__init __()' sia chiamato nello stesso thread – MestreLion

+0

È un mio errore con la parola "loop": intendo una chiamata veloce (implementata con un do ... loop) alla funzione start() Velocemente da essere più veloce dell'impostazione del flag "is_running" dalla funzione _run() – fdb

11

È possibile utilizzare schedule. Funziona su Python 2.7 e 3.3 ed è piuttosto leggero:

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) 
+1

perché while while ?, non funzionerebbe come cron job? – Jaydev

5

in base alla risposta MestreLion, si risolve un piccolo problema con il 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() 
4

Usa Celery.

from celery.task import PeriodicTask 
from datetime import timedelta 


class ProcessClicksTask(PeriodicTask): 
    run_every = timedelta(minutes=30) 

    def run(self, **kwargs): 
     #do something 
0

in base alla risposta di Alex Martelli, ho implementato versione decoratore che è più facile da integrata.

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 __name__ == '__main__': 
    print('start') 
    periodic_event() 
    print('end')