Executing periodic actions [duplicate]
I am working on Windows. I want to execute a function foo()
every 10 seconds.
How do I do this?
Solution 1:
At the end of foo()
, create a Timer
which calls foo()
itself after 10 seconds.
Because, Timer
create a new thread
to call foo()
.
You can do other stuff without being blocked.
import time, threading
def foo():
print(time.ctime())
threading.Timer(10, foo).start()
foo()
#output:
#Thu Dec 22 14:46:08 2011
#Thu Dec 22 14:46:18 2011
#Thu Dec 22 14:46:28 2011
#Thu Dec 22 14:46:38 2011
Solution 2:
Simply sleeping for 10 seconds or using threading.Timer(10,foo)
will result in start time drift. (You may not care about this, or it may be a significant source of problems depending on your exact situation.) There can be two causes for this - inaccuracies in the wake up time of your thread or execution time for your function.
You can see some results at the end of this post, but first an example of how to fix it. You need to track when your function should next be called as opposed to when it actually got called and account for the difference.
Here's a version that drifts slightly:
import datetime, threading
def foo():
print datetime.datetime.now()
threading.Timer(1, foo).start()
foo()
Its output looks like this:
2013-08-12 13:05:36.483580
2013-08-12 13:05:37.484931
2013-08-12 13:05:38.485505
2013-08-12 13:05:39.486945
2013-08-12 13:05:40.488386
2013-08-12 13:05:41.489819
2013-08-12 13:05:42.491202
2013-08-12 13:05:43.492486
2013-08-12 13:05:44.493865
2013-08-12 13:05:45.494987
2013-08-12 13:05:46.496479
2013-08-12 13:05:47.497824
2013-08-12 13:05:48.499286
2013-08-12 13:05:49.500232
You can see that the sub-second count is constantly increasing and thus, the start time is "drifting".
This is code that correctly accounts for drift:
import datetime, threading, time
next_call = time.time()
def foo():
global next_call
print datetime.datetime.now()
next_call = next_call+1
threading.Timer( next_call - time.time(), foo ).start()
foo()
Its output looks like this:
2013-08-12 13:21:45.292565
2013-08-12 13:21:47.293000
2013-08-12 13:21:48.293939
2013-08-12 13:21:49.293327
2013-08-12 13:21:50.293883
2013-08-12 13:21:51.293070
2013-08-12 13:21:52.293393
Here you can see that there is no longer any increase in the sub-second times.
If your events are occurring really frequently you may want to run the timer in a single thread, rather than starting a new thread for each event. While accounting for drift this would look like:
import datetime, threading, time
def foo():
next_call = time.time()
while True:
print datetime.datetime.now()
next_call = next_call+1;
time.sleep(next_call - time.time())
timerThread = threading.Thread(target=foo)
timerThread.start()
However your application will not exit normally, you'll need to kill the timer thread. If you want to exit normally when your application is done, without manually killing the thread, you should use
timerThread = threading.Thread(target=foo)
timerThread.daemon = True
timerThread.start()