11

I am writing a python script that needs to run a thread which listens to a network socket.

I'm having trouble with killing it using Ctrl+c using the code below:

#!/usr/bin/python

import signal, sys, threading

THREADS = []

def handler(signal, frame):
    global THREADS
    print "Ctrl-C.... Exiting"
    for t in THREADS:
        t.alive = False
    sys.exit(0)

class thread(threading.Thread):
    def __init__(self):
        self.alive = True
        threading.Thread.__init__(self)


    def run(self):
        while self.alive:
            # do something
            pass

def main():
    global THREADS
    t = thread()
    t.start()
    THREADS.append(t)

if __name__ == '__main__':
    signal.signal(signal.SIGINT, handler)
    main()

Appreciate any advise on how to catch Ctrl+c and terminate the script.

Mwiza
  • 7,780
  • 3
  • 46
  • 42
loloy.batoytoy
  • 129
  • 1
  • 1
  • 6

2 Answers2

10

The issue is that after the execution falls off the main thread (after main() returned), the threading module will pause, waiting for the other threads to finish, using locks; and locks cannot be interrupted with signals. This is the case in Python 2.x at least.

One easy fix is to avoid falling off the main thread, by adding an infinite loop that calls some function that sleeps until some action is available, like select.select(). If you don't need the main thread to do anything at all, use signal.pause(). Example:

if __name__ == '__main__':
    signal.signal(signal.SIGINT, handler)
    main()
    while True:           # added
        signal.pause()    # added
Armin Rigo
  • 12,048
  • 37
  • 48
6

It's because signals can only be caught by main thread. And here main thread ended his life long time ago (application is waiting for your thread to finish). Try adding

while True: 
    sleep(1)

to the end of your main() (and of course from time import sleep at the very top).

or as Kevin said:

for t in THREADS:
    t.join(1) # join with timeout. Without timeout signal cannot be caught.
jaor
  • 831
  • 7
  • 18
  • Using `Thread.join()` is preferable over spin-looping with timeouts. – Kevin Stone Oct 29 '13 at 08:18
  • @KevinStone - have you tried it? Replace my spin-looping with timeouts with `for t in THREADS: t.join()`. – jaor Oct 29 '13 at 08:24
  • Interesting. I didn't know a Thread.join starved the signal handlers. http://stackoverflow.com/questions/631441/interruptible-thread-join-in-python. Ironically, you can set a timeout for the join and the signal will be handled. Here's an example with join and a long timeout: https://gist.github.com/kevinastone/a95668df254af2451fb7 – Kevin Stone Oct 29 '13 at 08:38
  • 1
    Main thread did not close It's waiting for threads to join and come back to main thread – rezakamalifard Aug 02 '17 at 18:53