Hanpan
Hanpan

Reputation: 10251

Python - Cancel timer thread

I'm trying to create a method which runs on a timer in the background of my main script:

def hello_world(self):
        print 'Hello!'
        threading.Timer(2,hello_world).start()

if __name__ == "__main__":
    try:
        hello_world()
    except KeyboardInterrupt:
        print '\nGoodbye!'

I am getting this message when I attempt to keyboard interrupt my script:

Exception KeyboardInterrupt in <module 'threading' from '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/threading.py'> ignored

How do I close the thread so that I can exit my application cleanly?

Upvotes: 5

Views: 9176

Answers (3)

Chris Redford
Chris Redford

Reputation: 17768

You just need to set the Timer thread as a daemon

def hello_world(self):
    print 'Hello!'
    t = threading.Timer(2,hello_world)
    t.daemon = True
    t.start()

That will cause it to exit when the main thread exits, e.g. due to KeyboardInterrupt.

The daemon setting causes the entire program to exit when the only threads left are daemon threads.

Upvotes: 11

senderle
senderle

Reputation: 150957

To elaborate a bit on Aphex's answer, the main thread can't possibly catch the KeyboardInterrupt signal, unless you have very fast fingers. The main thread exits almost immediately! Try this:

import threading

def hello_world():
        print 'Hello!'
        threading.Timer(2,hello_world).start()

if __name__ == "__main__":
    try:
        hello_world()
    except KeyboardInterrupt:
        print '\nGoodbye!'
    print "main thread exited"

More generally, I would not suggest using a self-calling timer like this, just because it creates a lot of threads. Just create one thread and call time.sleep inside it.

However, as long as you keep the main thread running, you seem to be able to catch KeyboardInterrupt inside. The trick then is to make the thread a daemon thread that exits when the main thread exits.

import threading
import time

def hello_world():
    while(True):
        print 'Hello!'
        time.sleep(2)

if __name__ == "__main__":
    hw_thread = threading.Thread(target = hello_world)
    hw_thread.daemon = True
    hw_thread.start()
    try:
        time.sleep(1000)
    except KeyboardInterrupt:
        print '\nGoodbye!'

This exits automatically after 1000 seconds -- you could make that number even bigger if you like. You could also use a busy-loop to repeat the sleep call, but I don't really see the point.

Upvotes: 12

Aphex
Aphex

Reputation: 7490

Try re-raising the KeyboardInterrupt exception: http://effbot.org/zone/stupid-exceptions-keyboardinterrupt.htm

This still may not work though; chances are you're running into this caveat:

Threads interact strangely with interrupts: the KeyboardInterrupt exception will be received by an arbitrary thread. (When the signal module is available, interrupts always go to the main thread.)

In short, you can't be sure that the KeyboardInterrupt is going to your main thread. To workaround this, you may want to look into the signal module.

Edit: A more elegant way to cancel the thread is to have a shared variable that the thread looks at, and exits if it becomes false. Then if you want to kill the thread from your main thread, you set the variable to false.

Upvotes: 2

Related Questions