2017-07-04 26 views
10

Tôi đang làm việc trên một ứng dụng Django. Tôi có một điểm cuối API, nếu được yêu cầu, phải thực hiện một hàm phải được lặp lại một vài lần (cho đến khi một điều kiện nhất định là đúng). Cách tôi giải quyết vấn đề này ngay bây giờ là -Django - chạy một hàm mỗi x giây

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

    return True 

Trong khi tôi biết rằng đây là một cách tiếp cận khủng khiếp và tôi không nên chặn trong 2 giây, tôi không thể tìm ra cách để tránh .
Tác phẩm này hoạt động sau khi đợi 4 giây. Nhưng tôi muốn một cái gì đó mà giữ vòng lặp chạy trong nền, và dừng lại khi some_fn trả về True. (Ngoài ra, chắc chắn rằng some_fn sẽ trả về True)

EDIT -
Đọc phản hồi của Oz123 đã cho tôi ý tưởng dường như hoạt động. Dưới đây là những gì tôi đã làm -

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) 

Điều này thực hiện công việc cho tôi. Nó đơn giản nhưng tôi không biết làm thế nào đa luồng hiệu quả là kết hợp với Django.
Nếu bất cứ ai có thể chỉ ra những cạm bẫy của điều này, những lời chỉ trích được đánh giá cao.

+1

Bạn có thể sử dụng cần tây cho việc này. Bạn có thể tìm hướng dẫn sử dụng tại đây: https://realpython.com/blog/python/asynchronous-tasks-with-django-and-celery/#periodic-tasks – neverwalkaloner

+0

@neverwalkaloner nghe giống như chính xác những gì tôi cần. Sẽ cố gắng sử dụng nó, cảm ơn. :) – Zeokav

+1

Như đã đề cập bởi @neverwalkaloner, bạn có thể sử dụng cần tây, bạn có thể thiết lập nhiệm vụ định kỳ với lịch biểu, kiểm tra tài liệu là rất linh hoạt http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html cung cấp cho nó một cái nhìn để mô-đun crontab. –

Trả lời

5

Đối với nhiều dự án nhỏ cần tây là một trong giết. Đối với những dự án bạn có thể sử dụng schedule, nó rất dễ sử dụng.

Với thư viện này bạn có thể thực hiện bất kỳ chức năng thực hiện một nhiệm vụ theo định kỳ:

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) 

Ví dụ chạy một cách chặn, nhưng nếu bạn nhìn vào FAQ, bạn sẽ thấy rằng bạn cũng có thể chạy các nhiệm vụ trong một sợi song song, như vậy mà bạn không chặn, và loại bỏ các nhiệm vụ một lần không cần thiết nữa: từ lịch nhập khẩu 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 

Dưới đây là một ví dụ cho việc sử dụng trong một phương pháp học:

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

Tôi đã, trên thực tế, tìm cách tránh Celery (bây giờ) bởi vì tôi chỉ cần lập kế hoạch ở một hoặc hai nơi trong dự án của tôi. Tôi chỉ gặp một thứ như thế này haha! Tôi sẽ cố gắng thực hiện điều này và hoàn nguyên trở lại nếu tôi có bất kỳ câu hỏi nào. Cảm ơn bạn đã phản hồi! :) – Zeokav

+0

Tôi đã đăng câu trả lời bên dưới. Tôi đánh giá cao nếu bạn có thể cho tôi biết nếu điều đó có khả năng có thể gây ra vấn đề sau này. – Zeokav

+0

Quy ước thú vị - bất kỳ lý do gì khiến bạn không tạo ra một lớp kế thừa từ 'Scheduler' để thêm phương thức' run_continuously'? Nó sẽ làm cho những gì đang xảy ra một chút rõ ràng hơn ngay lập tức. – Tim

1

Tôi khuyên bạn nên sử dụng Celery's task management. Bạn có thể tham khảo this để thiết lập ứng dụng này (gói nếu bạn đến từ nền javaScript).

Khi thiết lập, bạn có thể thay đổi mã để:

@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 
Các vấn đề liên quan