Search code examples
pythonmultithreadingdaemon

Python Threading/Daemon


Self-taught programming student, so I apologize for all the amateur mistakes. I want to learn some deeper subjects, so I'm trying to understand threading, and exception handling.

import threading
import sys
from time import sleep
from random import randint as r

def waiter(n):
    print "Starting thread " + str(n)
    wait_time = r(1,10)
    sleep(wait_time)
    print "Exiting thread " + str(n)

if __name__=='__main__':
    try:
        for i in range(5):
            t = threading.Thread(target=waiter, args=(i+1,))
            t.daemon = True
            t.start()
            sleep(3)
        print 'All threads complete!'
        sys.exit(1)
    except KeyboardInterrupt:
        print ''
        sys.exit(1)

This script just starts and stops threads after a random time and will kill the program if it receives a ^C. I've noticed that it doesn't print when some threads finish:

Starting thread 1
Starting thread 2
Starting thread 3
Exiting thread 3
Exiting thread 2
Starting thread 4
Exiting thread 1
Exiting thread 4
Starting thread 5
All threads complete!

In this example, it never states it exits thread 5. I find I can fix this if I comment out the t.daemon = True statement, but then exception handling waits for any threads to finish up.

Starting thread 1
Starting thread 2
^C
Exiting thread 1
Exiting thread 2

I can understand that when dealing with threads, it's best that they complete what they're handling before exiting, but I'm just curious as to why this is. I'd really appreciate any answers regarding the nature of threading and daemons to guide my understanding.


Solution

  • The whole point of a daemon thread is that if it's not finished by the time the main thread finishes, it gets summarily killed. Quoting the docs:

    A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set through the daemon property or the daemon constructor argument.

    Note Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.

    Now, look at your logic. The main thread only sleeps for 3 seconds after starting thread 5. But thread 5 can sleep for anywhere from 1-10 seconds. So, about 70% of the time, it's not going to be finished by the time the main thread wakes up, prints "All threads complete!", and exits. But thread 5 is still asleep for another 5 seconds. In which case thread 5 will be killed without ever going to print "Exiting thread 5".

    If this isn't the behavior you want—if you want the main thread to wait for all the threads to finish—then don't use daemon threads.