Cancellable threading.Timer in Python

I am trying to write a method that counts down to a given time and unless a restart command is given, it will execute the task. But I don't think Python threading.Timer class allows for timer to be cancelable.

import threading

def countdown(action):
    def printText():
        print 'hello!'

    t = threading.Timer(5.0, printText)
    if (action == 'reset'):
        t.cancel()

    t.start()

I know the above code is wrong somehow. Would appreciate some kind guidance over here.


Solution 1:

You would call the cancel method after you start the timer:

import time
import threading

def hello():
    print "hello, world"
    time.sleep(2)

t = threading.Timer(3.0, hello)
t.start()
var = 'something'
if var == 'something':
    t.cancel()

You might consider using a while-loop on a Thread, instead of using a Timer.
Here is an example appropriated from Nikolaus Gradwohl's answer to another question:

import threading
import time

class TimerClass(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()
        self.count = 10

    def run(self):
        while self.count > 0 and not self.event.is_set():
            print self.count
            self.count -= 1
            self.event.wait(1)

    def stop(self):
        self.event.set()

tmr = TimerClass()
tmr.start()

time.sleep(3)

tmr.stop()

Solution 2:

I'm not sure if I understand correctly. Do you want to write something like in this example?

>>> import threading
>>> t = None
>>> 
>>> def sayHello():
...     global t
...     print "Hello!"
...     t = threading.Timer(0.5, sayHello)
...     t.start()
... 
>>> sayHello()
Hello!
Hello!
Hello!
Hello!
Hello!
>>> t.cancel()
>>>

Solution 3:

The threading.Timer class does have a cancel method, and although it won't cancel the thread, it will stop the timer from actually firing. What actually happens is that the cancel method sets a threading.Event, and the thread actually executing the threading.Timer will check that event after it's done waiting and before it actually executes the callback.

That said, timers are usually implemented without using a separate thread for each one. The best way to do it depends on what your program is actually doing (while waiting for this timer), but anything with an event loop, like GUI and network frameworks, all have ways to request a timer that is hooked into the eventloop.

Solution 4:

Im not sure if best option but for me is woking like this: t = timer_mgr(.....) append to list "timers.append(t)" and then after all created you can call:

for tm in timers:#threading.enumerate():
     print "********", tm.cancel()

my timer_mgr() class is this:

class timer_mgr():
def __init__(self, st, t, hFunction, id, name):
    self.is_list = (type(st) is list)
    self.st = st
    self.t = t
    self.id = id
    self.hFunction = hFunction      
    self.thread = threading.Timer(t, self.handle_function, [id])
    self.thread.name = name

def handle_function(self, id):
    if self.is_list:
        print "run_at_time:", datetime.now()
        self.hFunction(id)
        dt = schedule_fixed_times(datetime.now(), self.st)
        print "next:", dt
        self.t = (dt-datetime.now()).total_seconds()
    else:
        self.t = self.st        
        print "run_every", self.t, datetime.now()
        self.hFunction(id)

    self.thread = threading.Timer(self.t, self.handle_function, [id])
    self.thread.start()             

def start(self):
    self.thread.start()

def cancel(self):
    self.thread.cancel()