Подтвердить что ты не робот

Django - запускать функцию каждые x секунд

Я работаю над приложением Django. У меня есть конечная точка API, которая, если требуется, должна выполнять функцию, которая должна повторяться несколько раз (пока не будет выполнено какое-либо условие). Как я имею дело с этим сейчас -

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

  return True

Пока я знаю, что это ужасный подход и что я не должен блокироваться в течение 2 секунд, я не могу понять, как обойти его.
Это работает, скажем, в ожидании 4 секунд. Но я хотел бы что-то, что заставляет цикл работать в фоновом режиме, и остановится, как только some_fn вернет True. (Также, конечно, some_fn вернет True)

РЕДАКТИРОВАТЬ -
Чтение ответа Oz123 дало мне представление, которое, похоже, работает. Вот что я сделал -

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)

Это делает работу для меня. Это просто, но я не знаю, насколько эффективна многопоточность в сочетании с Django.
Если кто-то может указать на подводные камни этого, то критикуют.

4b9b3361

Ответ 1

Для многих небольших проектов сельдерей является излишним. Для этих проектов вы можете использовать график, он очень прост в использовании.

С помощью этой библиотеки вы можете периодически выполнять любую функцию:

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) 

Пример работает в режиме блокировки, но если вы посмотрите в FAQ, вы обнаружите, что вы также можете запускать задачи в параллельном потоке, так что вы не блокируете, и удалять задачу, когда она больше не нужна:

from schedule import Scheduler

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

Вот пример для использования в методе класса:

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

Ответ 2

Я рекомендую вам использовать Celery task. Вы можете сослаться на this, чтобы настроить это приложение (пакет, если вы используете фон javaScript).

После установки вы можете изменить код на:

@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