How to Use Python Daemon Threads

The Python Threading Module allows us to create new threads using the Thread Class. There are two different types of threads that can be created in Python, Daemon and non-daemon threads.

What is the difference between these two thread types? Let’s find out!


What are Non-Daemon Threads?

Before we talk about Daemon threads, lets discuss the behavior of regular threads in Python. By “regular thread” we mean a non-daemon thread.

Whenever a thread is created in Python, it is given a “task” or a “job” to complete in the form of a function. The thread proceeds to execute this function parallel to the main thread.

A non-daemon thread is “independent” of the main thread. If the main thread finishes execution and terminates the other threads that were created will continue execution until they are finished. You will see an example of this later on as well.

Regular threads should be used for operations which should not be interrupted until they are finished. For example, heavy computation or the execution of an algorithm etc.


What are Daemon Threads?

A daemon differs from a regular thread in the sense that it is “dependent” on the main thread. If the main thread finishes execution, the Daemon thread will be terminated even it has not finished executing.

This is beneficial to us in certain scenarios where we want a thread(s) to terminate if the main thread finishes execution. One example of such a scenario would be where we have a thread with an infinite loop that is waiting for input. If left alone, this thread would execute forever even after the main thread is finished. This is pointless as this thread has no value in existing beyond the lifetime of the main thread.

Hence the solution here is to make it a Daemon thread.

Other common examples of where Daemon threads are used:

  1. Background Tasks
  2. Loggers
  3. Garbage collection
  4. Performing periodic tasks

As you can see, most of the above tasks are non-critical (will not impact main thread execution) and are mostly background tasks.

Let’s take a look at some code now.


How to create a Daemon Thread?

Creating a Thread in Python is quite easy. First we need to import the Thread Class from the threading module.

from threading import Thread

Next we create a function which our thread will execute.

import time

def task():
    for i in range(10):
        print("Do some Random X Task")
        time.sleep(0.5)

And finally we use the Thread Class and pass the name of function into the target parameter. If the function had any arguments, we would pass them into the args parameter in the form of a tuple.

thread = threading.Thread(target = task)

So now we have our thread. But it hasn’t begun executing yet. And it’s not a Daemon thread either. (We’ll get to that in a bit).

To start the execution of this thread, we will call the start() method on the thread object.

thread.start()

Now our thread will begin execution.

Here is the complete code, with some additional print statements. We have also added a 2 second wait for the main thread before it finishes execution.

import threading
import time

def task():
    for i in range(10):
        print("Do some Random X Task")
        time.sleep(0.5)
    print("Thread has finished")

thread = threading.Thread(target = task)
thread.start()

time.sleep(2)
print("Main Thread finished")

Now observe the output carefully. This is what the output will look like on a regular thread. The new thread we created will continue its execution until it is complete. Its execution is not effected by the main thread.

Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Main Thread finished
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Thread has finished

In order to create a Daemon thread, we just need to make a single change to our thread object. During it’s creation we just have to pass the Boolean value of True to the daemon parameter.

thread = threading.Thread(target=task, daemon=True)

If we run the code from earlier with a Daemon thread instead, we get the following output.

Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Main Thread finished

As you can see here, the Daemon Thread we created terminated itself when the main thread finished.


Daemon Threads Examples

Let’s take a look at another example involving Daemon threads in Python.

The below code features two threads (other the main thread). The first thread is the same as the previous code examples which executes the task() function. We have renamed this to the job_thread. The second thread is responsible for taking input, so we have called it the input_thread.

def task():
    for i in range(10):
        print("Do some Random X Task")
        time.sleep(0.5)
    print("Thread has finished")

def take_input():
    while True:
        userInput = input()
        print(userInput)

job_thread = threading.Thread(target=task)
input_thread = threading.Thread(target=take_input, daemon=True)

In most cases, and in most applications, taking input is an infinite process. A common example is in Client-Server networks, where two separate threads are created (one for receiving data, and one for sending data). Similarly our take_input() function in the above code also waits for inputs infinitely.

If the input_thread is created as a non-daemon thread, it will never stop its execution even after the main thread ends. This is pointless, as there no thread left to process the input. It’s also a waste of resources.

But if its a Daemon thread, it will terminate itself when the main thread finishes execution. The below code is a more “complete” version of this example.

import threading
import time

userInput = ""

def task():
    for i in range(10):
        print("Do some Random X Task")
        time.sleep(0.5)
    print("Thread has finished")

def take_input():
    global userInput
    while True:
        userInput = input()
        print(userInput)

job_thread = threading.Thread(target=task)
input_thread = threading.Thread(target=take_input, daemon=True)

job_thread.start()
input_thread.start()

job_thread.join()

A few interesting things to note here. First, we made userInput global. This is because, realistically speaking, the other threads should have a way of accessing the input from the input_thread. (Even though we aren’t gonna do anything with it in this example)

Secondly, the more important feature here is the .join() method used on the job_thread. The join() method forces the main thread to wait until the job_thread is finished. This is important because if this wasn’t there, the input_thread would terminate before the job_thread. And in a scenario where job_thread relied on input from input_thread, this would cause problems.

The whole point of this example was basically to show you show Daemon and non-daemon threads can be used together in Python.


Other Daemon Methods

There are two other methods associated with Daemon Threads in Python. Let’s take a look at how we can use them.

The first method is isDaemon(), which can be used to check whether a certain thread is a “Daemon” thread or not. It will return True if the thread is a Daemon, otherwise it will return False.

The below code shows this in practice.

thread = threading.Thread(target=task, daemon=True)
print(thread.isDaemon())
True

Now with a non-daemon thread.

thread = threading.Thread(target=task)
print(thread.isDaemon())
False

The second method available to us is setDaemon(). This method can be used to convert a regular Thread into a Daemon thread.

Normally we can only declare a Thread as a Daemon during its creation (initialization) with the Thread Class. setDaemon() is a way of converting a regular thread (that has already been created) to a Daemon Thread.

Here is a small code snippet for the setDaemon() method.

thread = threading.Thread(target=task)
thread.setDaemon(True)
thread.start()

time.sleep(2)
print("Main Thread finished")

As you can see, it gives us the same output as the Daemon code thread from earlier. (It’s using the same function from earlier)

Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Main Thread finished

There is however one major restriction. You cannot convert a thread that has already begun it’s execution with the start() method.

thread = threading.Thread(target=task)
thread.start()
thread.setDaemon(True)
RuntimeError: cannot set daemon status of active thread
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Do some Random X Task
Thread has finished

As you can see from the output of the above code, this will return a runtime error. The main thread crashed, so there is no output from there. The thread exists separately and was created before the main thread crashed, so it continued and completed it’s exection.



This marks the end of the How to Use Python Daemon Threads Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments