Thursday, July 8, 2021

Python daemon threads

Daemons are only useful when the main program is running, and it's okay to kill them off once the other non-daemon threads have exited. Without daemon threads, we have to keep track of them, and tell them to exit, before our program can completely quit. By setting them as daemon threads, we can let them run and forget about them, and when our program quits, any daemon threads are killed automatically.


Usually our main program implicitly waits until all other threads have completed their work. However, sometimes programs spawn a thread as a daemon that runs without blocking the main program from exiting. Using daemon threads is useful for services where there may not be an easy way to interrupt the thread or where letting the thread die in the middle of its work without losing or corrupting data. To designate a thread as a daemon, we call its setDaemon() method with a boolean argument. The default setting for a thread is non-daemon. So, passing True turns the daemon mode on.



import threading

import time

import logging


logging.basicConfig(level=logging.DEBUG,

                    format='(%(threadName)-9s) %(message)s',)


def n():

    logging.debug('Starting')

    logging.debug('Exiting')


def d():

    logging.debug('Starting')

    time.sleep(5)

    logging.debug('Exiting')


if __name__ == '__main__':


t = threading.Thread(name='non-daemon', target=n)


d = threading.Thread(name='daemon', target=d)

d.setDaemon(True)


d.start()

t.start()



As we can see from the output, it does not have "Exiting" message from the daemon thread, since all of the non-daemon threads (including the main thread) exit before the daemon thread wakes up from its five second sleep.



To wait until a daemon thread has completed its work, we may want to use join() method.


d.join()

t.join()



By default, join() blocks indefinitely. In our sample, join() blocks the calling thread (main thread) until the threads (d / t) whose join() method is called is terminated - either normally or through an unhandled exception - or until the optional timeout occurs.

We can also pass a timeout argument which is a float representing the number of seconds to wait for the thread to become inactive. If the thread does not complete within the timeout period, join() returns anyway.



When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, we must call isAlive() after join() to decide whether a timeout happened - if the thread is still alive, the join() call timed out.


d.join(3.0)

    print 'd.isAlive()', d.isAlive()

    t.join()



The output Will be 


(daemon   ) Starting

(non-daemon) Starting

(non-daemon) Exiting

d.isAlive() True


However, if we set the timeout 7 seconds:


the daemon wakes up during the period and exits, and we will have the following output:


(daemon   ) Starting

(non-daemon) Starting

(non-daemon) Exiting

(daemon   ) Exiting

d.isAlive() False




References:

https://www.bogotobogo.com/python/Multithread/python_multithreading_Daemon_join_method_threads.php

No comments:

Post a Comment