68

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?

starball
  • 20,030
  • 7
  • 43
  • 238
aF.
  • 64,980
  • 43
  • 135
  • 198
  • Your program is terminating after spawning the first Timer. – Thomas Wouters Feb 08 '10 at 17:15
  • In order to cancel the Timer you need to keep a reference to it somewhere, and call its `cancel` method. – Thomas Wouters Feb 08 '10 at 17:42
  • Note that all the solutions posted so far suffer from a (small) cumulative buildup of error. That is, rather than calculating the next sleep duration based on a fixed starting time, they simply sleep for "another 60s". This will never sleep for less than 60s, and will usually sleep for slightly more than 60s. This isn't always a problem, but if you want to sleep exactly 1440 times in a day, make sure you compensate for this error. – Peter Hansen Feb 09 '10 at 01:52
  • that's not a problem in my solution. thanks for pointing that out! – aF. Feb 09 '10 at 09:02
  • related: [`setTimeout()`, `setInterval()` in Python. Examples for tkinter, twisted, gtk](http://stackoverflow.com/a/14040516/4279) – jfs Jul 25 '13 at 17:47

8 Answers8

108

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

import threading

def f(f_stop):
    # do something here ...
    if not f_stop.is_set():
        # call f() again in 60 seconds
        threading.Timer(60, f, [f_stop]).start()

f_stop = threading.Event()
# start calling f now and every 60 sec thereafter
f(f_stop)

# stop the thread when needed
#f_stop.set()
João Cartucho
  • 3,688
  • 32
  • 39
David Underhill
  • 15,896
  • 7
  • 53
  • 61
  • with that code it only executes one time, is there something missing to execute more times? – aF. Feb 08 '10 at 17:15
  • 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 Feb 08 '10 at 17:23
  • and how can I cancel the threading.Timer after the end of program's execution? – aF. Feb 08 '10 at 17:37
  • 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 Feb 08 '10 at 17:51
  • tks m8 that's the way to do it :) – aF. Feb 08 '10 at 19:57
  • 2
    @DavidUnderhill @aF. thank you for the help. I ran into the same problem, where the function continued running despite me ending the program. Where should the ``sys.exit(0)`` go in my code? In the function body, or after calling the function – the line `f()` in this case? – zch Nov 03 '12 at 15:44
  • Should probably put the call to threading at the top of the function not the bottom in case the user code in f() takes some non-trivial length of time to complete – MattCochrane Jul 29 '17 at 01:23
  • Will this keep increasing the stack every time it's called? Does the variable created by `threading.Timer(60, f).start()` live in the function `f()` or is it somehow external to that? Seems like you are in a way, recursively calling `f()`, this could be very bad if the timeout was smaller, like every second or 20ms or something. – MattCochrane Jul 29 '17 at 01:25
  • Lovely... I was unnecessarily trying to start and terminate threads using multiprocessing. Of course, it may be a better approach, but for some reason I faced a lot of trouble calling functions in sub process. I'm working on a POC and this seemed to do the trick for now. – Fr0zenFyr Mar 20 '18 at 10:12
4

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

import time
from threading import Thread

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


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

timer = BackgroundTimer()
timer.start()

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

Michael M.
  • 10,486
  • 9
  • 18
  • 34
0xfe
  • 4,541
  • 1
  • 18
  • 14
  • I hope you realize the empty `__init__` method is superfluous :) – Thomas Wouters Feb 08 '10 at 16:52
  • 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 Feb 08 '10 at 17:31
3

I googled around and found the Python circuits Framework, which makes it possible to wait
for a particular event.

The .callEvent(self, event, *channels) method of circuits contains a fire and suspend-until-response functionality, the documentation says:

Fire the given event to the specified channels and suspend execution until it has been dispatched. This method may only be invoked as argument to a yield on the top execution level of a handler (e.g. "yield self.callEvent(event)"). It effectively creates and returns a generator that will be invoked by the main loop until the event has been dispatched (see :func:circuits.core.handlers.handler).

I hope you find it as useful as I do :)
./regards

Pehat
  • 1,573
  • 1
  • 10
  • 24
synner
  • 105
  • 7
2

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
  • 130,178
  • 23
  • 148
  • 122
  • 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 Feb 08 '10 at 17:21
2

I think the right way to run a thread repeatedly is the next:

import threading
import time

def f():
    print("hello world")  # your code here
    myThread.run()

if __name__ == '__main__':
    myThread = threading.Timer(3, f)  # timer is set to 3 seconds
    myThread.start()
    time.sleep(10)  # it can be loop or other time consuming code here
    if myThread.is_alive():
        myThread.cancel()

With this code, the function f is executed every 3 seconds within the 10 seconds time.sleep(10). At the end running of thread is canceled.

syviad
  • 230
  • 2
  • 7
1

Why dont you create a dedicated thread, in which you put a simple sleeping loop:

#!/usr/bin/env python
import time
while True:
   # Your code here
   time.sleep(60)
ayhan
  • 70,170
  • 20
  • 182
  • 203
Aif
  • 11,015
  • 1
  • 30
  • 44
1

If you want to invoke the method "on the clock" (e.g. every hour on the hour), you can integrate the following idea with whichever threading mechanism you choose:

import time

def wait(n):
    '''Wait until the next increment of n seconds'''
    x = time.time()
    time.sleep(n-(x%n))
    print(time.asctime())
Guillaume Jacquenot
  • 11,217
  • 6
  • 43
  • 49
0

[snip. removed non async version]

To use asyncing you would use trio. I recommend trio to everyone who asks about async python. It is much easier to work with especially sockets. With sockets I have a nursery with 1 read and 1 write function and the write function writes data from an deque where it is placed by the read function; and waiting to be sent. The following app works by using trio.run(function,parameters) and then opening an nursery where the program functions in loops with an await trio.sleep(60) between each loop to give the rest of the app a chance to run. This will run the program in a single processes but your machine can handle 1500 TCP connections insead of just 255 with the non async method.

I have not yet mastered the cancellation statements but I put at move_on_after(70) which is means the code will wait 10 seconds longer than to execute a 60 second sleep before moving on to the next loop.

import trio
async def execTimer():
    '''This function gets executed in a nursery simultaneously with the rest of the program'''
        while True:
            trio.move_on_after(70):
                await trio.sleep(60)
                print('60 Second Loop')

async def OneTime_OneMinute():
    '''This functions gets run by trio.run to start the entire program'''
    with trio.open_nursery() as nursery:
        nursery.start_soon(execTimer)
        nursery.start_soon(print,'do the rest of the program simultaneously')


def start():
    '''You many have only one trio.run in the entire application'''
    trio.run(OneTime_OneMinute)

if __name__ == '__main__':
    start()

This will run any number of functions simultaneously in the nursery. You can use any of the cancellable statements for checkpoints where the rest of the program gets to continue running. All trio statements are checkpoints so use them a lot. I did not test this app; so if there are any questions just ask. As you can see trio is the champion of easy-to-use functionality. It is based on using functions instead of objects but you can use objects if you wish. Read more at: [1]: https://trio.readthedocs.io/en/stable/reference-core.html

bauderr
  • 47
  • 10