문제

I read somewhere that KeyboardInterrupt exception is only raised in the main thread in Python. I also read that the main thread is blocked while the child thread executes. So, does this mean that CTRL+C can never reach to the child thread. I tried the following code:

def main():
    try:
        thread = threading.Thread(target=f)
        thread.start()  # thread is totally blocking (e.g., while True)
        thread.join()
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

In this case there is no effect of CTRL+C on the execution. It is like it is not able to listen to the signal. Am I understanding this the wrong way? Is there any other way to kill the thread using CTRL+C?

도움이 되었습니까?

해결책

The problem there is that you are using thread1.join(), which will cause your program to wait until that thread finishes to continue.

The signals will always be caught by the main process, because it's the one that receives the signals, it's the process that has threads.

Doing it as you show, you are basically running a 'normal' application, without thread features, as you start 1 thread and wait until it finishes to continue.

다른 팁

If you want to have main thread to receive the CTRL+C signal while joining, it can be done by adding timeout to join() call.

The following seems to be working (don't forget to add daemon=True if you want main to actually end):

thread1.start()
while True:
    thread1.join(600)
    if not thread1.isAlive():
        break

In Python, it is true that KeyboardInterrupt exceptions are raised only in the main thread of each process. But as other answers mentionned, it is also true that the method Thread.join blocks the calling thread, including KeyboardInterrupt exceptions. That is why Ctrl+C seems to have no effect: the execution in the main thread remains blocked at the line thread.join().

So a simple solution to your question is to firstly, add a timeout argument to thread.join() and put that call in a loop that ends when the child thread exits, so that KeyboardInterrupt exceptions can be raised after each timeout, and secondly, make the child thread daemonic, which means that its parent (the main thread here) will kill it when it exits (only non-daemon threads are not killed but joined when their parent exits):

def main():
    try:
        thread = threading.Thread(target=f, daemon=True)  # create a daemon child thread
        thread.start()

        while thread.is_alive():
            thread.join(1)  # join shortly to not block KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

But a better solution, if you control the child thread's code, is to inform the child thread to exit gracefully (instead of abruptly like with the first solution), for instance with a threading.Event:

def main():
    try:
        event = threading.Event()
        thread = threading.Thread(target=f, args=(event,))
        thread.start()
        event.wait()  # wait forever but without blocking KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        event.set()  # inform the child thread that it should exit
        sys.exit(1)

def f(event):
    while not event.is_set():
        pass  # do the actual work
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top