kaajaa328
kaajaa328

Reputation: 61

Main thread not catching KeyboardInterrupt

I've seen several threads on here about killing python threads in a clean way, but I think I'm having a more fundamental issue. Suppose I have some code that looks like this:

t1 = threading.Thread(target=method1, args=(args1,))
t1.daemon = True

t2 = threading.Thread(target=method2, args=(args2, ))
t2.daemon = True
t1.start()
t2.start()

while True:
    time.sleep(1)

I would like for the main thread to notice a Ctrl-C keyboard interrupt, and from there I can handle the termination of the other threads (t1 and t2) as appropriate given the context. But no matter what I do, I can't get the main thread to catch the KeyboardInterrupt. I've tried something like this:

try:
    while True: time.sleep(100)
except KeyboardInterrupt:
    print "Quitting!"

Or something like this:

threads = []
threads.append(t1)
threads.append(t2)
while len(threads) > 0:
    try:
        threads = [t for t in threads if t is not None and t.isAlive()]
        time.sleep(1)
    except:
        print "Ctrl - C received, kiling"
        for t in threads:
            t.kill_received = True

But none of these even print the messages in the exception handler, displaying only this:

    Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/local/lib/python2.7/dist-packages/audioread/gstdec.py", line 149, in run
    self.loop.run()
  File "/usr/lib/python2.7/dist-packages/gi/overrides/GLib.py", line 576, in run
    raise KeyboardInterrupt
KeyboardInterrupt

The main question here is not how to kill t1 and t2 safely (I've got to deal with that too, eventually), but why is the main thread not catching KeyboardInterrupt here?

Edit: As I've written the examples, I've already tried the approach involving sleeping in a while loop within the try-except block. Any other ideas?

Upvotes: 1

Views: 1153

Answers (1)

martineau
martineau

Reputation: 123453

The following works. Even though the try...except in the Thread subclass is never triggered, the main thread is able to catch KeyboardInterrupts.

The code in the run() method of the MyThread subclass is a modified version of what is the CPython 2.7 source for the Thread.run() method, which already contained a try/finally...to which I added an except BaseException as exc: in order to print a message when any type of one happens.

import threading
import time

def method1(args):
    print('method1() running')
    while True:
        time.sleep(.001)

def method2(args):
    print('method2() running')
    while True:
        time.sleep(.01)


class MyThread(threading.Thread):
    def __init__(self, *args, **kwargs):
        super(MyThread, self).__init__(*args, **kwargs)

    def run(self):
        """ Method representing the thread's activity.

        You may override this method in a subclass. The standard run() method
        invokes the callable object passed to the object's constructor as the
        target argument, if any, with sequential and keyword arguments taken
        from the args and kwargs arguments, respectively.
        """
        try:
            print('in MyThread.run()\n')
            if self._Thread__target:
                self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
        except BaseException as exc:
            print('reraising Exception {}'.format(exc))
            raise typeof(exc)(str(exc))
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._Thread__target, self._Thread__args, self._Thread__kwargs
            print('exiting')

args1 = 1
t1 = MyThread(target=method1, args=(args1,))
t1.daemon = True

args2 = 2
t2 = MyThread(target=method2, args=(args2,))
t2.daemon = True
t1.start()
t2.start()

try:
    while True:
        time.sleep(1)
except BaseException as exc:
    print('exception "{}" occurred'.format(type(exc)))
    quit()

Console output (I typed Ctrl-C right after the method1() running appeared):

> python not-catching-keyboardinterrupt.py
in MyThread.run()
in MyThread.run()

method2() running

method1() running
exception "<type 'exceptions.KeyboardInterrupt'>" occurred

Upvotes: 2

Related Questions