1

I could not interrupt my threaded Python production app using Ctrl+C on Windows, it continues running, tried both exception and signal handling. Here is very simplified version of code, that does not interrupts. Single thread app terminates fine, same as multithreaded Linux version. Could anybody help with this trouble? Thanks in advance.

import threading
import time

class FooThread(threading.Thread):
    stop_flag = False

    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while not self.stop_flag:
            print(1)
            time.sleep(1)

t = FooThread()
t.start()

try:
    t.join()
except KeyboardInterrupt:
    t.stop_flag = True
    t.join()
3
  • You made your thread a daemon but you also need to keep your "main" thread alive to listen for a signal for instance. Commented May 8, 2018 at 16:38
  • There's nothing like POSIX signals in Windows to asynchronously interrupt any wait on a kernel dispatcher object, which in this case is the semaphore that's waited on to acquire a lock in the join method. Internally, the wait could be modified in two ways to allow interruption in the main thread. Either switch to using WaitForMultipleObjects and include an Event that gets set by the SIGINT handler (which gets called by the C runtime's console control handler) or switch to using an alertable WaitForSingleObjectEx wait and have the SIGINT handler queue an APC to alert the main thread. Commented May 8, 2018 at 16:50
  • I hesitate to submit a patch to address this because, for performance reasons, there's pressure to use native condition variables instead of the currently-used emulated condition variables based on Semaphore kernel objects. It's not obvious how the console control handler could interrupt the main thread if the condition-variable implementation uses SleepConditionVariableSRW and WakeConditionVariable. Whatever it takes will likely involve a major rewrite. Commented May 8, 2018 at 17:06

1 Answer 1

3

You made your thread a daemon but you also need to keep your "main" thread alive to listen for a signal or keyboard interrupt

A simple working implementation with a signal:

import threading
import time
import sys
import signal

class FooThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while not self.stop_flag:
            print(1)
            time.sleep(1)

    stop_flag = False

def main():
    t = FooThread()
    def signal_handler(signal, frame):
        print('You pressed Ctrl+C!')
        t.stop_flag = True
        t.join()

    signal.signal(signal.SIGINT, signal_handler)
    t.start()

    while not t.stop_flag:
        time.sleep(1)

if __name__ == "__main__":
    main()
Sign up to request clarification or add additional context in comments.

2 Comments

"daemon" has a specific meaning with regard to Python threads, and the OP did not make a daemon=True thread. Anyway, this is one possible workaround. Alternatively, the join can be implemented in a loop with a small timeout (e.g. t.join(0.1)) to allow the main thread to execute the SIGINT handler (but this is vulnerable to a bug if join is interrupted between acquiring and releasing the lock). One could also set a new console control handler via SetConsoleCtrlHandler and have it handle cleanup and exit the process in the control thread, and not worry about the main thread.
@Fluffy, thanks for answer, it works. One note - pass in while is bad, it leads to Python 40% CPU usage on my machine. If replace it with sleep(1), Python CPU usage is only 2%, so it is better choice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.