Fundamentals 9 min read

How to Terminate Background Threads in Python: Daemon Threads and Event Objects

This article explains why Python background threads cannot be forcibly killed, and demonstrates two practical methods—using daemon threads and threading.Event objects—to enable graceful termination and proper cleanup, including code examples, signal handling, and interrupt behavior.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
How to Terminate Background Threads in Python: Daemon Threads and Event Objects

When trying to stop a background thread in Python, many discover that threads cannot be killed directly; the interpreter waits for non‑daemon threads to finish before exiting.

Threaded Example
<code>import random
import threading
import time

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())  # do some work...
    print(f'{i} iterations completed before exiting.')

th = threading.Thread(target=bg_thread)
th.start()
th.join()
</code>

Running the program and pressing Ctrl‑C during the first interrupt does not stop the background thread; a second Ctrl‑C forces the process to exit because Python aborts after the second signal.

One way to allow the program to exit cleanly is to make the worker thread a daemon by setting daemon = True before starting it. Daemon threads do not block interpreter shutdown, so the first Ctrl‑C terminates the process immediately.

<code>import random
import threading
import time

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())
    print(f'{i} iterations completed before exiting.')

th = threading.Thread(target=bg_thread)
th.daemon = True
th.start()
th.join()
</code>

Another robust approach is to use a threading.Event object. The main thread sets the event when it receives a signal, and the worker thread periodically checks the event (or waits on it) to exit gracefully, allowing cleanup code to run.

<code>import random
import signal
import threading
import time

exit_event = threading.Event()

def bg_thread():
    for i in range(1, 30):
        print(f'{i} of 30 iterations...')
        time.sleep(random.random())
        if exit_event.is_set():
            break
    print(f'{i} iterations completed before exiting.')

def signal_handler(signum, frame):
    exit_event.set()

signal.signal(signal.SIGINT, signal_handler)
th = threading.Thread(target=bg_thread)
th.start()
th.join()
</code>

Using event.wait(timeout=...) can replace the explicit time.sleep call, providing an interruptible sleep that returns immediately when the event is set.

<code>for i in range(1, 30):
    print(f'{i} of 30 iterations...')
    if exit_event.wait(timeout=random.random()):
        break
</code>

In conclusion, Python threads cannot be forcibly killed; instead, make them daemon or cooperate with an Event to achieve graceful termination.

concurrencyeventthreadingdaemonGraceful Shutdown
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.