2017-07-04 7 views
10

Ich arbeite an einer Django App. Ich habe einen API-Endpunkt, der bei Bedarf eine Funktion ausführen muss, die einige Male wiederholt werden muss (bis eine bestimmte Bedingung erfüllt ist). Wie ich mit ihm jetzt zu tun habe ist -Django - führe alle x Sekunden eine Funktion aus

def shut_down(request): 
    # Do some stuff 
    while True: 
    result = some_fn() 
    if result: 
     break 
    time.sleep(2) 

    return True 

Während ich weiß, dass dies ein schrecklicher Ansatz ist und dass ich nicht für 2 Sekunden blockiert, kann ich nicht herausfinden, wie um es zu bekommen .
Dies funktioniert, nach einer Wartezeit von 4 Sekunden. Aber ich möchte etwas, das die Schleife im Hintergrund läuft, und stoppt, sobald some_fn True zurückgibt. (Außerdem ist es sicher, dass some_fn wird Wahr zurück)

EDIT -
Lesen Oz123 Antwort gab mir eine Idee, die zu funktionieren scheint. Hier ist, was ich tat -

def shut_down(params): 
    # Do some stuff 
    # Offload the blocking job to a new thread 

    t = threading.Thread(target=some_fn, args=(id,), kwargs={}) 
    t.setDaemon(True) 
    t.start() 

    return True 

def some_fn(id): 
    while True: 
     # Do the job, get result in res 
     # If the job is done, return. Or sleep the thread for 2 seconds before trying again. 

     if res: 
      return 
     else: 
      time.sleep(2) 

Dies macht den Job für mich. Es ist einfach, aber ich weiß nicht, wie effizient Multithreading in Verbindung mit Django ist.
Wenn jemand auf Fallstricke hinweisen kann, wird Kritik geschätzt.

+1

Sie können Sellerie dafür verwenden. Sie können Handbuch hier finden: https://realpython.com/blog/python/asynchronous-tasks-with-django-and-sellery/#periodic-tasks – neverwalkaloner

+0

@newalkaloner klingt genau, was ich brauche. Werde es versuchen, danke. :) – Zeokav

+1

Wie von @newalkaloner erwähnt, können Sie Sellerie verwenden, können Sie periodische Aufgabe mit Zeitplan einrichten, überprüfen Sie das Dokument ist sehr flexibel http://docs.selleryproject.org/en/latest/userguide/periodic-tasks.html Werfen Sie einen Blick auf Crontab Modul. –

Antwort

5

Für viele kleine Projekte Sellerie ist ein über Sieg:

Einmal eingestellt, können Sie den Code ändern. Für diese Projekte können Sie schedule verwenden, es ist sehr einfach zu bedienen.

Mit dieser Bibliothek können Sie jede Funktion eine Aufgabe periodisch ausführen lassen können:

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) 
schedule.every().monday.do(job) 
schedule.every().wednesday.at("13:15").do(job) 

while True: 
    schedule.run_pending() 
    time.sleep(1) 

Das Beispiel wird in einer blockierenden Art und Weise, aber wenn Sie in den FAQ anschauen, werden Sie feststellen, dass Sie auch Aufgaben ausführen können in ein Parallelgewinde, so dass Sie nicht blockiert werden, und sobald die Aufgabe entfernen nicht mehr benötigt: import Scheduler aus Zeitplan

def run_continuously(self, interval=1): 
    """Continuously run, while executing pending jobs at each elapsed 
    time interval. 
    @return cease_continuous_run: threading.Event which can be set to 
    cease continuous run. 
    Please note that it is *intended behavior that run_continuously() 
    does not run missed jobs*. For example, if you've registered a job 
    that should run every minute and you set a continuous run interval 
    of one hour then your job won't be run 60 times at each interval but 
    only once. 
    """ 

    cease_continuous_run = threading.Event() 

    class ScheduleThread(threading.Thread): 

     @classmethod 
     def run(cls): 
      while not cease_continuous_run.is_set(): 
       self.run_pending() 
       time.sleep(interval) 

    continuous_thread = ScheduleThread() 
    continuous_thread.setDaemon(True) 
    continuous_thread.start() 
    return cease_continuous_run 


Scheduler.run_continuously = run_continuously 

Hier ist ein Beispiel für die Verwendung in einer Klassenmethode:

def foo(self): 
     ... 
     if some_condition(): 
      return schedule.CancelJob # a job can dequeue it 

    # can be put in __enter__ or __init__ 
    self._job_stop = self.scheduler.run_continuously() 

    logger.debug("doing foo"...) 
    self.foo() # call foo 
    self.scheduler.every(5).seconds.do(
     self.foo) # schedule foo for running every 5 seconds 

    ... 
    # later on foo is not needed any more: 
    self._job_stop.set() 

    ... 

    def __exit__(self, exec_type, exc_value, traceback): 
     # if the jobs are not stop, you can stop them 
     self._job_stop.set() 
+0

Ich war in der Tat auf der Suche nach Möglichkeiten, Sellerie (für jetzt) ​​zu vermeiden, weil ich nur an ein oder zwei Orten in meinem Projekt planen muss. Ich bin gerade auf so etwas gestoßen, haha! Ich werde versuchen, dies zu implementieren und zurück, wenn ich irgendwelche Fragen habe. Danke für die Antwort! :) – Zeokav

+0

Ich habe eine Antwort unten gepostet. Ich würde es begrüßen, wenn Sie mir sagen könnten, ob das möglicherweise später Probleme verursachen kann. – Zeokav

+0

Interessante Konvention - Gibt es einen Grund, warum Sie keine Klasse erstellen, die von 'Scheduler' ableitet, die die Methode' run_continuous' hinzufügt? Es würde das, was vor sich geht, etwas deutlicher machen. – Tim

1

Ich empfehle Ihnen, Celery task Management zu verwenden. Sie können beziehen this, um diese App einzurichten (Paket, wenn Sie von Java-Hintergrund stammen).

@app.task 
def check_shut_down(): 
    if not some_fun(): 
     # add task that'll run again after 2 secs 
     check_shut_down.delay((), countdown=3) 
    else: 
     # task completed; do something to notify yourself 
     return True 
Verwandte Themen