Executing periodic actions in Python [duplicate]

Posted on

Solving problem is about exposing yourself to as many situations as possible like Executing periodic actions in Python [duplicate] and practice these strategies over and over. With time, it becomes second nature and a natural way you approach any problems in general. Big or small, always start with a plan, use other strategies mentioned here till you are confident and ready to code the solution.
In this post, my aim is to share an overview the topic about Executing periodic actions in Python [duplicate], which can be followed any time. Take easy to follow this discuss.

Executing periodic actions in Python [duplicate]

I am working on Windows. I want to execute a function foo() every 10 seconds.

How do I do this?

Asked By: Bruce

||

Answer #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
Answered By: kev

Answer #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()
Answered By: Michael Anderson

Answer #3:

Surprised to not find a solution using a generator for timing. I just designed this one for my own purposes.

This solution: single threaded, no object instantiation each period, uses generator for times, rock solid on timing down to precision of the time module (unlike several of the solutions I’ve tried from stack exchange).

Note: for Python 2.x, replace next(g) below with g.next().

import time
def do_every(period,f,*args):
    def g_tick():
        t = time.time()
        while True:
            t += period
            yield max(t - time.time(),0)
    g = g_tick()
    while True:
        time.sleep(next(g))
        f(*args)
def hello(s):
    print('hello {} ({:.4f})'.format(s,time.time()))
    time.sleep(.3)
do_every(1,hello,'foo')

Results in, for example:

hello foo (1421705487.5811)
hello foo (1421705488.5811)
hello foo (1421705489.5809)
hello foo (1421705490.5830)
hello foo (1421705491.5803)
hello foo (1421705492.5808)
hello foo (1421705493.5811)
hello foo (1421705494.5811)
hello foo (1421705495.5810)
hello foo (1421705496.5811)
hello foo (1421705497.5810)
hello foo (1421705498.5810)
hello foo (1421705499.5809)
hello foo (1421705500.5811)
hello foo (1421705501.5811)
hello foo (1421705502.5811)
hello foo (1421705503.5810)

Note that this example includes a simulation of the cpu doing something else for .3 seconds each period. If you changed it to be random each time it wouldn’t matter. The max in the yield line serves to protect sleep from negative numbers in case the function being called takes longer than the period specified. In that case it would execute immediately and make up the lost time in the timing of the next execution.

Answered By: watsonic

Answer #4:

Perhaps the sched module will meet your needs.

Alternatively, consider using a Timer object.

Answered By: Raymond Hettinger

Answer #5:

This will insert a 10 second sleep in between every call to foo(), which is approximately what you asked for should the call complete quickly.

import time
while True:
    foo()
    time.sleep(10)

To do other things while your foo() is being called in a background thread

import time
import sys
import threading
def foo():
    sys.stdout.write('({}) foon'.format(time.ctime()))
def foo_target():
    while True:
        foo()
        time.sleep(10)
t = threading.Thread(target=foo_target)
t.daemon = True
t.start()
print('doing other things...')
Answered By: wim

Answer #6:

Here’s a nice implementation using the Thread class: http://g-off.net/software/a-python-repeatable-threadingtimer-class

the code below is a little more quick and dirty:

from threading import Timer
from time import sleep
def hello():
    print "hello, world"
    t = Timer(3,hello)
    t.start()
t = Timer(3, hello)
t.start() # after 3 seconds, "hello, world" will be printed
# timer will wake up ever 3 seconds, while we do something else
while True:
    print "do something else"
    sleep(10)
Answered By: Richard Logwood

Answer #7:

You can execute your task in a different thread. threading.Timer will let you execute a given callback once after some time has elapsed, if you want to execute your task, for example, as long as the callback returns True (this is actually what glib.timeout_add provides, but you might not have it installed in windows) or until you cancel it, you can use this code:

import logging, threading, functools
import time
logging.basicConfig(level=logging.NOTSET,
                    format='%(threadName)s %(message)s')
class PeriodicTimer(object):
    def __init__(self, interval, callback):
        self.interval = interval
        @functools.wraps(callback)
        def wrapper(*args, **kwargs):
            result = callback(*args, **kwargs)
            if result:
                self.thread = threading.Timer(self.interval,
                                              self.callback)
                self.thread.start()
        self.callback = wrapper
    def start(self):
        self.thread = threading.Timer(self.interval, self.callback)
        self.thread.start()
    def cancel(self):
        self.thread.cancel()
def foo():
    logging.info('Doing some work...')
    return True
timer = PeriodicTimer(1, foo)
timer.start()
for i in range(2):
    time.sleep(2)
    logging.info('Doing some other work...')
timer.cancel()

Example output:

Thread-1 Doing some work...
Thread-2 Doing some work...
MainThread Doing some other work...
Thread-3 Doing some work...
Thread-4 Doing some work...
MainThread Doing some other work...

Note: The callback isn’t executed every interval execution. Interval is the time the thread waits between the callback finished the last time and the next time is called.

Answered By: jcollado

Answer #8:

Here’s a simple single threaded sleep based version that drifts, but tries to auto-correct when it detects drift.

NOTE: This will only work if the following 3 reasonable assumptions are met:

  1. The time period is much larger than the execution time of the function being executed
  2. The function being executed takes approximately the same amount of time on each call
  3. The amount of drift between calls is less than a second

from datetime import timedelta
from datetime import datetime
def exec_every_n_seconds(n,f):
    first_called=datetime.now()
    f()
    num_calls=1
    drift=timedelta()
    time_period=timedelta(seconds=n)
    while 1:
        time.sleep(n-drift.microseconds/1000000.0)
        current_time = datetime.now()
        f()
        num_calls += 1
        difference = current_time - first_called
        drift = difference - time_period* num_calls
        print "drift=",drift
Answered By: Carl

Leave a Reply

Your email address will not be published.