Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python program with thread can't catch CTRL+C

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.

like image 429
loloy.batoytoy Avatar asked Oct 29 '13 07:10

loloy.batoytoy


People also ask

What does catch Ctrl C do in Python?

Signal handling: Catch Ctrl-C in Python Python allows us to set up signal -handlers so when a particular signal arrives to our program we can have a behavior different from the default. For example when you run a program on the terminal and press Ctrl-C the default behavior is to quit the program.

What is Ctrl-C in Python?

Ctrl-C Python allows us to set up signal -handlers so when a particular signal arrives to our program we can have a behavior different from the default. For example when you run a program on the terminal and press Ctrl-C the default behavior is to quit the program.

Why can't I press the Ctrl+C key in Python?

It's because of the design of the Python interpreter and interactive session. Ctrl + C sends a signal, SIGINT, to the Python process, which the Python interpreter handles by raising the KeyboardInterrupt exception in the currently-running scope.

What is wrong with threading in Python?

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.


2 Answers

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.
like image 192
jaor Avatar answered Sep 22 '22 18:09

jaor


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
like image 36
Armin Rigo Avatar answered Sep 22 '22 18:09

Armin Rigo