Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Daemon Threads

In this lesson, you’ll learn about daemon threads. The main difference between a regular thread and a daemon thread is that the main thread will not wait for daemon threads to complete before exiting. If you download the sample code, you can get your own copy of 04-daemon.py:

Download

Sample Code (.zip)

12.9 KB

To learn more, you can also check out the documentation.

00:00 In the previous lesson, you created a thread and you saw how the main thread didn’t wait for the new thread to finish before it printed that final line main ended.

00:11 Sometimes we do want the main thread to wait for all of our threads to finish before we hit that final line in our program, and one way we can do that is by using daemon threads.

00:23 daemon is a word referring to a background process or something that kind of hums along silently in the background while you’re doing something else.

00:33 An example of a daemon process could be something like maybe a Django web server that you’re running in the background, that when you’re down in your terminal, you don’t see it running, but you know that you can hit localhost:8080 or whatever and access that. So, it’s a process that’s running, but it’s not necessarily up front and in your face all the time.

00:54 It’s a daemon process, it’s in the background. For this example, I’m actually just going to use the exact same code from our previous lesson.

01:03 I’m going to copy that and just bring it into a new file. I’m calling mine 04-daemon.py.

01:11 And we’re just going to make a slight difference, but before we do that, let’s execute our program again. This time I’ll do 04-daemon and just verify—yep, we’re seeing main ended from our main thread while t is sleeping, up here. And then we should see myfunc ended here in a moment—there it is, and we’re back to our prompt.

01:35 This time we’re going to add one more argument to this threading object and it’s called daemon. And by default it’s False, but we’re going to make it True and see how this affects our code.

01:53 Okay, so right away we see a difference. There was no sleep(). The execution was really fast. So let’s break it down, let’s see. This is our __main__ entry point, so we see main started.

02:05 Then we created t and this is now a daemon thread. So we started t,

02:14 t went up and it printed myfunc started with realpython and then it went to sleep. And while t was asleep, then the main thread was able to continue on and it printed main ended, but there was no sleep() there, and I’ll execute it again. There was no sleep().

02:31 It didn’t get a chance to finish. By adding daemon=True, it doesn’t let the thread necessarily finish before the main thread ends.

02:43 What daemon=True provides is the ability to end our program when the main thread ends, but that could be dangerous because maybe our threads—and we only have one extra thread, but we might have many threads—maybe these threads aren’t done doing what they need to do, and it can have a very abrupt ending, like we’re seeing here.

03:07 We don’t get to sleep for 10 seconds. So, sometimes you want to do this, and sometimes you don’t. But this is an option—by adding daemon=True, you can do this. In later videos, we’re going to learn how we can actually wait for threads to complete before we finish our main program.

Avatar image for Alvaro Formoso

Alvaro Formoso on April 2, 2020

Hello! One quick question from my side: In the previous video, without using Daemon, for me (With Spyder Interpreter and Python 3.7.1) it was doing already the behaviour that you got with daemon=True. I mean, I could write in the console more things just after Main Ended sentence is written on the console.

Is that an update from the library?

Thanks! And Good job, the webpage is very cool! :)

Avatar image for Aniruddha

Aniruddha on Nov. 8, 2022

So why do we have deamon thread if it does not let finish other thread?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on Nov. 8, 2022

@Aniruddha Regular threads keep the Python interpreter running as long as there’s at least one non-daemon thread alive. On the other hand, Python doesn’t wait until your daemon threads finish. The interpreter will stop them abruptly when the last non-daemon thread—usually the main thread—finishes, even without letting the try..finally clause run, which would usually execute unconditionally. Therefore, you should never acquire any resources in daemon threads to avoid memory leaks and other problems such as deadlocks. They’re best used as lightweight background tasks, for example, to monitor something or send a heartbeat to a monitoring software.

Avatar image for haidergill

haidergill on June 7, 2026

Hi,

I tried the code without daemon threads and the main thread ended but the worker thread myfunc kept on going:

20260607-08:54:57.619:INFO:MainThread:main:3.14.5 free-threading build (tags/v3.14.5:5607950, May 10 2026, 10:44:56) [MSC v.1944 64 bit (AMD64)]
20260607-08:54:57.620:INFO:MainThread:main:2026-06-07 09:54:57.620416   GIL is currently disabled.
20260607-08:54:57.620:INFO:MainThread:main:Main started
20260607-08:54:57.621:INFO:MainThread:main:Main ending
20260607-08:54:57.621:INFO:Thread-1 (myfunc):myfunc:Myfunc started, thread=(Thread-1 (myfunc))
20260607-08:55:02.622:INFO:Thread-1 (myfunc):myfunc:Myfunc ended, thread=(Thread-1 (myfunc))

I then added the daemon=true clause and it now no longer launches MyFunc:

20260607-08:56:39.863:INFO:MainThread:main:3.14.5 free-threading build (tags/v3.14.5:5607950, May 10 2026, 10:44:56) [MSC v.1944 64 bit (AMD64)]
20260607-08:56:39.864:INFO:MainThread:main:2026-06-07 09:56:39.864087   GIL is currently disabled.
20260607-08:56:39.864:INFO:MainThread:main:Main started
20260607-08:56:39.865:INFO:MainThread:main:Main ending

Code:

def main() -> None:
    #get a logger
    obj_applog: logging.Logger = create_return_logger('MainDaemonLog',
                                                      debug=True,
                                                      str_log_dir='F:\\AppLog',
                                                      rotate_logfile=False)
    # get the python version and GIL status and log
    str_python_version: str = str(sys.version)
    obj_applog.info(str_python_version)
    str_gil_ft_status: str = check_gil_status()
    obj_applog.info(str_gil_ft_status)
    obj_applog.info('Main started')
    print('Main started')
    obj_myfunc_thread = Thread(target=myfunc,
                               args=['realpytyhon', obj_applog],
                               daemon=True)
    obj_myfunc_thread.start()
    obj_applog.info("Main ending")
    print('Main ended')


if __name__ == '__main__':
        main()
Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on June 11, 2026

Hi @haidergill, that’s the expected behavior, not a bug. It’s exactly what the daemon flag is meant to do.

A regular (non-daemon) thread keeps the interpreter alive: Python won’t exit until every non-daemon thread has finished. That’s why your first run shows Main ending and then Myfunc ended about five seconds later. The main thread returned, but the interpreter waited for the worker to wake from its sleep and finish.

A daemon thread is the opposite. Python does not wait for it. The moment the last non-daemon thread (your main thread) returns, the interpreter shuts down and any daemon threads are killed on the spot. In your daemon=True run, main() does almost nothing after .start(), so it returns almost instantly and the process tears down before the daemon thread logs anything. That’s why you see no Myfunc started line at all.

So the thread did launch (.start() ran), it just got terminated before it could do its work. In the lesson, the instructor’s daemon thread happens to print Myfunc started before the abrupt exit (around 2:31), but that’s a race. Whether the daemon prints anything depends on whether it gets scheduled before the main thread finishes, and on your machine it lost that race.

If you want to watch the daemon thread run to completion, keep the main thread alive long enough, for example by joining it:

obj_myfunc_thread.start()
obj_myfunc_thread.join()  # block until the thread finishes

The course covers .join() in a later lesson for exactly this. That said, daemon threads are really meant for fire-and-forget background work (a heartbeat, a monitor) that you’re fine abandoning when the program exits. As I noted in an earlier comment here, it’s best to avoid acquiring resources inside a daemon thread, since its cleanup (even a try/finally) may not get to run.

One last thing: the free-threading build and the disabled GIL don’t change any of this. Daemon semantics are the same with or without the GIL.

Become a Member to join the conversation.