Reputation: 61
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
Reputation: 123453
The following works. Even though the try...except
in the Thread
subclass is never triggered, the main thread is able to catch KeyboardInterrupt
s.
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