Is there a way to stop timer creep?

2019-08-03 23:57发布

问题:

I am looking for a way to run a method every second, regardless of how long it takes to run. In looking for help with that, I ran across

Run certain code every n seconds

and in trying it, found that it doesn't work correctly. It appears to have the very problem I'm trying to avoid: drift. I tried adding a "sleep(0.5)" after the print, and it does in fact slow down the loop, and the interval stays at the 1.003 (roughly) seconds.

Is there a way to fix this, to do what I want?

(venv) 20170728-153445 mpeck@bilbo:~/dev/whiskerlabs/aphid/loadtest$ cat a.py
import threading
import time
def woof():
  threading.Timer(1.0, woof).start()
  print "Hello at %s" % time.time()
woof()
(venv) 20170728-153449 mpeck@bilbo:~/dev/whiskerlabs/aphid/loadtest$ python a.py
Hello at 1501281291.84
Hello at 1501281292.85
Hello at 1501281293.85
Hello at 1501281294.85
Hello at 1501281295.86
Hello at 1501281296.86
Hello at 1501281297.86
Hello at 1501281298.87
Hello at 1501281299.87
Hello at 1501281300.88
Hello at 1501281301.88
Hello at 1501281302.89
Hello at 1501281303.89
Hello at 1501281304.89
Hello at 1501281305.89
Hello at 1501281306.9
Hello at 1501281307.9
Hello at 1501281308.9
Hello at 1501281309.91
Hello at 1501281310.91
Hello at 1501281311.91
Hello at 1501281312.91
Hello at 1501281313.92
Hello at 1501281314.92
Hello at 1501281315.92
Hello at 1501281316.93

回答1:

  1. Don't use a threading.Timer if you don't actually need a new thread each time; to run a function periodically sleep in a loop will do (possibly in a single separate thread).

  2. Whatever method you use to schedule the next execution, don't wait for the exact amount of time you use as interval - execution of the other statements take time, so the result is drift as you can see. Instead, write down the initial time in a variable, and at each iteration calculate the next time when you want to schedule execution and sleep for the difference between now and then.

    interval = 1.
    next_t = time.time()
    while True:
        next_t += interval
        time.sleep(next_t - time.time())
        # do whatever you want to do 
    

    (of course you may refine it for better overall accuracy, but this at least should avoid drift)



回答2:

I'm pretty sure the problem with that code is that it takes Python some time (apparently around .3s) to execute the call to your function woof, instantiate a new threading.Timer object, and print the current time. So basically, after your first call to the function, and the creation of a threading.Timer, Python waits exactly 1s, then calls the function woof (a decisecond or so), creates a new Timer object (yet another decisecond at least), and finally prints the current time with some delay.

The solution to actually run a program every second seems to be the Twisted library, as said on this other post, but I didn't really try it myself...

Edit: I would mark the question as possible duplicate but I apparently don't have enough reputation to do that yet... If someone can be kind enough to do so with at least the link I provided, it would be cool :)