How to create a background threaded on interval function call in python?
Asked Answered
E

4

5

I am trying to implement a heartbeat call that works in the background. How do I create a threaded on interval call of say every 30 seconds, which calls the following function:

self.mqConn.heartbeat_tick()

Also how would I stop this thread?

Many thanks.

Estreat answered 21/4, 2015 at 10:17 Comment(0)
G
9

Use a thread containing a loop

from threading import Thread
import time

def background_task():
    while not background_task.cancelled:
        self.mqConn.heartbeat_tick()
        time.sleep(30)
background_task.cancelled = False

t = Thread(target=background_task)
t.start()

background_task.cancelled = True

Alternatively, you could subclass timer, to make cancelling easy:

from threading import Timer

class RepeatingTimer(Timer):
    def run(self):
        while not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
            self.finished.wait(self.interval)


t = RepeatingTimer(30.0, self.mqConn.heartbeat_tick)
t.start() # every 30 seconds, call heartbeat_tick

# later
t.cancel() # cancels execution
Gemot answered 21/4, 2015 at 10:26 Comment(5)
It may be important to mention that this won't execute every 30 seconds if heartbeat_tick() takes a significant amount of timeCalfskin
Yep, this will be at least 30 seconds every time. At most 30 seconds is a fair bit harder.Gemot
If your foreground function was CPU bound, would the background thread be prevented from ever executing by the GIL?Titty
@JeffWidman: CPU-bound and "doesn't release the GIL" are not always the same thing. Yes, if the foreground thread held onto the GIL for a minute at a time, then I imagine the background thread would be delayed.Gemot
Thanks @Eric, I did a little more reading on David Beazley's lectures re: the GIL and makes more sense.Titty
C
3

Or you could use the Timer class in the threading module:

from threading import Timer

def hello():
    print "hello, world"

t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
t.cancel() # cancels execution, this only works before the 30 seconds is elapsed

This will not start every x seconds, rather it delays the thread for execution in x seconds. But you can still put that in a loop and use t.is_alive() to see its status.

Conferral answered 21/4, 2015 at 10:34 Comment(3)
Where would the loop go?Gemot
That would depend on the use case?Conferral
@Eric, feel free to improve my post if you're not satisfied with it.Conferral
A
3

A quick followup to Eric's answer: you can't subclass Timer in python 2, since it's actually a light function wrapper around the true class: _Timer. If you do you'll get the issue that pops up in this post.

Using _Timer instead fixes it:

from threading import _Timer

class RepeatingTimer(_Timer):
    def run(self):
        while not self.finished.is_set():
            self.function(*self.args, **self.kwargs)
            self.finished.wait(self.interval)


t = RepeatingTimer(30.0, self.mqConn.heartbeat_tick)
t.start() # every 30 seconds, call heartbeat_tick

# later
t.cancel() # cancels execution
Anecdotage answered 9/11, 2017 at 17:43 Comment(2)
I wouldn't be surprised if _Timer was subject to breaking changes through versions, while Timer is not (because of the leading prefix "privacy"). I had no issues subclassing Timer in 3.7, and the documentation says it's a subclass of Thread.Hayott
@Hayott you're likely correct - good comment. I should've been specific that this was with respect to 2.7.Anecdotage
N
2

One way to do this would be to use the circuits application framework like this:

from circuits import Component, Event, Timer


class App(Component):

    def init(self, mqConn):
        self.mqConn = mqConn
        Timer(30, Event.create("heartbeat"), persist=True).register(self)

    def heartbeat(self):
        self.mqConn.heartbeat_tick()


App().run()

Note: I'm the author of circuits :)

This is just a basic idea and structure -- You would need to adapt this to suit your exact application and requirements!

Nephritic answered 21/4, 2015 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.