My KeyboardInterrupt is only getting caught 90% of the time. What am I failing at?

1.4k views Asked by At

Here's some slimmed down code that demonstrates my use of threading:

import threading
import Queue
import time

def example():
    """ used in MainThread as the example generator """

    while True:
        yield 'asd'

class ThreadSpace:
    """ A namespace to be shared among threads/functions """

    # set this to True to kill the threads
    exit_flag = False

class MainThread(threading.Thread):

    def __init__(self, output):

        super(MainThread, self).__init__()

        self.output = output

    def run(self):

        # this is a generator that contains a While True
        for blah in example():
            self.output.put(blah)

            if ThreadSpace.exit_flag:
                break

            time.sleep(0.1)

class LoggerThread(threading.Thread):

    def __init__(self, output):

        super(LoggerThread, self).__init__()

        self.output = output

    def run(self):

        while True:
            data = self.output.get()

            print data

def main():

    # start the logging thread
    logging_queue  = Queue.Queue()
    logging_thread = LoggerThread(logging_queue)

    logging_thread.daemon = True
    logging_thread.start()

    # launch the main thread
    main_thread = MainThread(logging_queue)
    main_thread.start()

    try:
        while main_thread.isAlive():
            time.sleep(0.5)
    except KeyboardInterrupt:
        ThreadSpace.exit_flag = True

if __name__ == '__main__':
    main()

I have one main thread which gets data yielded to it from a blocking generator. In the real code, this generator yields network related data it sniffs out over the socket.

I then have a logging, daemon, thread which prints the data to screen.

To exit the program cleanly, I'm catching a KeyboardInterrupt which will set an exit_flag to try - This tells the main thread to return.

9 times out of 10, this will work fine. The program will exit cleanly. However, there's a few occasions when I'll receive the following two errors:

Error 1:

^CTraceback (most recent call last):
  File "demo.py", line 92, in <module>
    main('')
  File "demo.py", line 87, in main
    time.sleep(0.5)
KeyboardInterrupt

Error 2:

Exception KeyboardInterrupt in <module 'threading' from '/usr/lib/python2.7/threading.pyc'> ignored

I've run this exact sample code a few times and haven't been able to replicate the errors. The only difference between this and the real code is the example() generator. This, like I said, yields network data from the socket.

Can you see anything wrong with how I'm handling the threads?

1

There are 1 answers

1
dfichter On

KeyboardInterrupts are received by arbitrary threads. If the receiver isn't the main thread, it dies, the main thread is unaffected, ThreadSpace.exit_flag remains false, and the script keeps running.

If you want sigint to work, you can have each thread catch KeyboardInterrupt and call thread.interrupt_main() to get Python to exit, or use the signal module as the official documentation explains.