What is the best way to repeatedly execute a function every x seconds?

后端 未结 18 2732
不知归路
不知归路 2020-11-21 06:04

I want to repeatedly execute a function in Python every 60 seconds forever (just like an NSTimer in Objective C). This code will run as a daemon and is effectively like call

18条回答
  •  忘了有多久
    2020-11-21 06:34

    import time, traceback
    
    def every(delay, task):
      next_time = time.time() + delay
      while True:
        time.sleep(max(0, next_time - time.time()))
        try:
          task()
        except Exception:
          traceback.print_exc()
          # in production code you might want to have this instead of course:
          # logger.exception("Problem while executing repetitive task.")
        # skip tasks if we are behind schedule:
        next_time += (time.time() - next_time) // delay * delay + delay
    
    def foo():
      print("foo", time.time())
    
    every(5, foo)
    

    If you want to do this without blocking your remaining code, you can use this to let it run in its own thread:

    import threading
    threading.Thread(target=lambda: every(5, foo)).start()
    

    This solution combines several features rarely found combined in the other solutions:

    • Exception handling: As far as possible on this level, exceptions are handled properly, i. e. get logged for debugging purposes without aborting our program.
    • No chaining: The common chain-like implementation (for scheduling the next event) you find in many answers is brittle in the aspect that if anything goes wrong within the scheduling mechanism (threading.Timer or whatever), this will terminate the chain. No further executions will happen then, even if the reason of the problem is already fixed. A simple loop and waiting with a simple sleep() is much more robust in comparison.
    • No drift: My solution keeps an exact track of the times it is supposed to run at. There is no drift depending on the execution time (as in many other solutions).
    • Skipping: My solution will skip tasks if one execution took too much time (e. g. do X every five seconds, but X took 6 seconds). This is the standard cron behavior (and for a good reason). Many other solutions then simply execute the task several times in a row without any delay. For most cases (e. g. cleanup tasks) this is not wished. If it is wished, simply use next_time += delay instead.

提交回复
热议问题