2017-07-04 16 views
10

Pracuję nad aplikacją Django. Mam punkt końcowy interfejsu API, który na żądanie musi wykonać funkcję, która musi zostać powtórzona kilka razy (aż do spełnienia określonego warunku). Jak mam do czynienia z tym teraz jest -Django - uruchom funkcję co x sekund

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

    return True 

Choć wiem, że to jest straszne podejście i że nie powinno być zablokowanie przez 2 sekundy, nie mogę dowiedzieć się, jak poruszać się po nim .
Działa to po powiedzeniu 4 sekund oczekiwania. Ale chciałbym coś, co utrzymuje pętlę w tle, i zatrzymaj się, gdy some_fn zwróci True. (Jest także pewne, że some_fn zwróci True)

EDIT -
Czytanie odpowiedzi Oz123 dało mi pomysł, który wydaje się działać. Oto co zrobiłem -

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) 

To nie praca dla mnie. To proste, ale nie wiem, jak wydajna wielowątkowość jest w połączeniu z Django.
Jeśli ktoś może wskazać na pułapki tego, krytyka jest doceniana.

+1

Możesz użyć do tego selera. Możesz znaleźć instrukcję tutaj: https://realpython.com/blog/python/asynchronous-tasks-with-django-and-celery/#periodic-tasks – neverwalkaloner

+0

@ Oncewalkaloner brzmi jak dokładnie to, czego potrzebuję. Spróbuję go użyć, dzięki. :) – Zeokav

+1

Jak wspomniano przez @neverwalkaloner można użyć selera, można skonfigurować okresowe zadanie z harmonogramem, sprawdź, czy dokument jest bardzo elastyczny http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html popatrz na moduł crontab. –

Odpowiedz

5

Dla wielu małych projektów seler to kill skończona. W przypadku tych projektów można użyć schedule, jest bardzo łatwy w użyciu.

Z tej biblioteki można dokonać dowolnej funkcji wykonać zadanie cyklicznie:

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) 

Przykład przebiega w sposób blokujący, ale jeśli spojrzeć w FAQ, można zauważyć, że można również uruchomić zadania wątek równolegle, tak że nie blokują i usunąć zadanie nie raz już potrzebne: od importu harmonogramu harmonogram

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 

Oto przykład do wykorzystania w metodzie klasy:

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

Tak naprawdę szukałem sposobów na uniknięcie Celery (na razie), ponieważ potrzebuję planowania tylko w jednym lub dwóch miejscach w moim projekcie. Właśnie natknąłem się na coś takiego, haha! Spróbuję to zaimplementować i wrócić, jeśli będę miał jakieś pytania. Dziękuję za odpowiedź! :) – Zeokav

+0

Poniżej zamieściłem odpowiedź. Byłbym wdzięczny, gdybyś mógł mi powiedzieć, czy to może później spowodować problemy. – Zeokav

+0

Interesująca konwencja - jakikolwiek powód, dla którego nie tworzysz klasy, która dziedziczy po 'Scheduler', który dodaje metodę' run_continuously'? To sprawi, że to, co się dzieje, stanie się nieco bardziej oczywiste. – Tim

1

Polecam używać zarządzania Celery task. Możesz polecić this, aby skonfigurować tę aplikację (pakiet, jeśli jesteś z javaScript w tle).

Po ustawieniu można zmieniać kod do:

@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