views:

576

answers:

4

Hello,

I want to execute a function every 60 seconds on Python but I don't want to be blocked meanwhile.

How can I do it asynchronously?

import threading
import time

def f():
    print("hello world")
    threading.Timer(3, f).start()

if __name__ == '__main__':
    f()    
    time.sleep(20)

With this code, the function f is executed every 3 seconds within the 20 seconds time.time. At the end it gives an error and I think that it is because the threading.timer has not been canceled.

How can I cancel it?

Thanks in advance!

A: 

Why dont you create a dedicated thread, in which you put a

while true:
   stuff_todo
   sleep 60
Aif
That's not Python.
Mike Graham
No but that's the idea. When I answered, there was no code, so that was just an idea.
Aif
+7  A: 

You could try the threading.Timer class: http://docs.python.org/library/threading.html#timer-objects.

def f():
    # do something here ...
    # call f() again in 60 seconds
    threading.Timer(60, f).start()

# start calling f now and every 60 sec thereafter
f()
David Underhill
with that code it only executes one time, is there something missing to execute more times?
aF
You need to `.start()` the Timer, I've edited it in.
THC4k
Oops, thanks for adding the .start() ... I should've copied/pasted from my interpreter :). Now it should really get called every 60 seconds rather than just once.
David Underhill
and how can I cancel the threading.Timer after the end of program's execution?
aF
If the program is ending, then calling sys.exit(0) will cause the thread to terminate too.Alternatively, you could use an external boolean variable which f() reads. Then f() could decide whether or not to create another timer based on its value. sys.exit will be more immediate though (since it will immediately halt the other thread, rather than waiting another interval before f() runs again).
David Underhill
tks m8 that's the way to do it :)
aF
A: 

The simplest way is to create a background thread that runs something every 60 seconds. A trivial implementation is:

class BackgroundTimer(Thread):   
   def run(self):
      while 1:
        Time.sleep(60)
        # do something


# ... SNIP ...
# Inside your main thread
# ... SNIP ...

timer = BackgroundTimer()
timer.start()

Obviously, this if the "do something" takes a long time, you'll need to accommodate for it in your sleep statement. But this serves as a good approximation.

0xfe
I hope you realize the empty `__init__` method is superfluous :)
Thomas Wouters
Of course... fixed :-) Thanks.
0xfe
The way to be independent of execution time is to use a real time clock (say, `time.time()` or `datetime.now()`), check the time at the start of task execution, add 60 seconds to get the time for the next firing, then check again at the end and subtract that from the next firing time to get your sleep time.
Mike DeSimone
+2  A: 

It depends on what you actually want to do in the mean time. Threads are the most general and least preferred way of doing it; you should be aware of the issues with threading when you use it: not all (non-Python) code allows access from multiple threads simultaneously, communication between threads should be done using thread-safe datastructures like Queue.Queue, you won't be able to interrupt the thread from outside it, and terminating the program while the thread is still running can lead to a hung interpreter or spurious tracebacks.

Often there's an easier way. If you're doing this in a GUI program, use the GUI library's timer or event functionality. All GUIs have this. Likewise, if you're using another event system, like Twisted or another server-process model, you should be able to hook into the main event loop to cause it to call your function regularly. The non-threading approaches do cause your program to be blocked while the function is pending, but not between functioncalls.

Thomas Wouters
The trap with doing this on a GUI timer is that the GUI is frozen while you handle the timeout. If the task is short, no problem. If not, then your program appears to hang every minute.
Mike DeSimone