Search code examples
pythonmultithreadingpython-2.7daemonsys

Python threading script is not starting/working properly


I quickly wrote this example script for stackoverflow so ignore the functionality aspect of it (my version looks alot better than this), but:

from threading import Thread
from msvcrt import getch       #I'm using Windows by the way.
from time import sleep
import sys

def KeyEvent():
    while True:
        key = getch()
        if key.encode('hex') == '03': #^C
            y = raw_input("Are you sure you want to quit? (y/n): ").lower()
            if y == 'y':
                sys.exit(0)
            else: pass

def main():
    t = 0
    while True:
        print "The count is {0}".format(t)
        t +=1
        sleep(1)

if __name__ == "__main__":
    mainthread = Thread(target = main)
    kev = Thread(target = KeyEvent)

    mainthread.daemon = True
    kev.daemon = True

    mainthread.start()
    kev.start()

What this script is supposed to do is run both loops at the same time, one counts up in seconds, while the other checks for ^C. Please don't recommend that I use a different method, because this is an example.

My problem is, that the script just doesn't start. It displays "The count is 0" and exits, but if I comletely omit the .daemon = True parts, the script runs, but it doesn't run sys.exit(0) correctly. How can I make this script run correctly AND exit when prompted?


Solution

  • Threads marked as daemon automatically die when every other non-daemon thread is dead. In your case, the main thread dies just after calling start() on your two daemon threads, bringing the python process with it. That anything gets done in your thread is a matter of luck and timing.

    sys.exit(0) does not kill threads other than the main thread. You need a way to signal your threads that it's time to stop. One way to do that is through an Event object.

    You shouldn't use getch to try to catch Ctrl+C, try using a signal handler instead:

    from threading import Thread
    from threading import Event
    from time import sleep
    import signal
    
    stop = Event()
    
    def handler(signum, frame):
        y = raw_input("Are you sure you want to quit? (y/n): ").lower()
        if y == 'y':
            stop.set()
    
    def main():
        t = 0
        while not stop.isSet():
            print "The count is {0}".format(t)
            t +=1
            sleep(1)
    
    if __name__ == "__main__":
        signal.signal(signal.SIGINT, handler)
    
        mainthread = Thread(target = main)
        mainthread.start()
    
        while mainthread.is_alive():
            try:
                mainthread.join(timeout = 0.1)
            except IOError:
                pass #Gets thrown when we interrupt the join