Last Updated on September 12, 2022
You can stop a thread by using a threading.Event.
In this tutorial you will discover how to gracefully stop a thread in Python.
Let’s get started.
Need to Stop a Thread
A thread is a thread of execution in a computer program.
Every Python program has at least one thread of execution called the main thread. Both processes and threads are created and managed by the underlying operating system.
Sometimes we may need to create additional threads in our program in order to execute code concurrently.
Python provides the ability to create and manage new threads via the threading module and the threading.Thread class.
You can learn more about Python threads in the guide:
In concurrent programming, we may run a task in a new thread, then later decide to stop the task.
This may be for many reasons, such as:
- The result from the task is no longer required.
- The application is shutting down.
- The outcome from the task has gone astray.
Python provides no way to stop a running thread in the API.
How can we stop a thread in Python?
Run loops using all CPUs, download your FREE book to learn how.
How to Stop a Thread
A thread can be stopped using a shared boolean variable such as a threading.Event.
A threading.Event is a thread-safe boolean variable flag that can be either set or not set. It can be shared between threads and checked and set without fear of a race condition.
If you are new to threading.Events, you can learn more here:
A new event can be created and then shared between threads, for example:
1 2 3 |
... # create a shared event event = Event() |
The event is created in the ‘not set‘ or False state.
We may have a task in a custom function that is run in a new thread. The task may iterate, such as in a while-loop or a for-loop.
For example:
1 2 3 4 5 |
# custom task function def task(): # perform task in iterations while True: # ... |
We can update our task function to check the status of an event each iteration.
If the event is set true, we can exit the task loop or return from the task() function, allowing the new thread to terminate.
The status of the threading.Event can be checked via the is_set() function.
For example:
1 2 3 4 5 6 7 8 |
# custom task function def task(): # perform task in iterations while True: # ... # check for stop if event.is_set(): break |
The main thread, or another thread, can then set the event in order to stop the new thread from running.
The event can be set or made True via the set() function.
For example:
1 2 3 4 5 |
... # set the event event.set() # wait for the new thread to stop thread.join() |
Now that we know how to stop a Python thread, let’s look at some worked examples.
Example of Stopping a Thread With a Custom Function
In this section we can explore how to stop a thread that is executing a custom function.
First, we will develop an example that runs a custom function in a separate thread, then we will look at how to update the example to stop the thread on demand.
Custom Function in a New Thread
We can develop an example that runs a function in a new thread.
The new task function will execute in a loop and once finished the new thread will terminate and the main thread will terminate.
Firstly, we can define the task function.
The function will execute a loop five times. Each iteration it will block for a second, then report a message in an effort to simulate work and show progress.
Once the task is finished, the function will report a final message.
The task() function below implements this.
1 2 3 4 5 6 7 8 9 |
# custom task function def task(): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # report a message print('Worker thread running...') print('Worker closing down') |
Next, in the main thread we can create a new threading.Thread instance that is configured to execute our task() function in a new thread via the “target” argument.
1 2 3 |
... # create and configure a new thread thread = Thread(target=task) |
We can then immediately start the new thread via the start() function.
1 2 3 |
... # start the new thread thread.start() |
The main thread will then wait for the new thread to finish by joining the thread.
If you are new to joining threads, you can learn more here:
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# SuperFastPython.com # example of a thread executing a custom function from time import sleep from threading import Thread # custom task function def task(): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # report a message print('Worker thread running...') print('Worker closing down') # create and configure a new thread thread = Thread(target=task) # start the new thread thread.start() # wait for the new thread to finish thread.join() |
Running the example first creates and starts the new thread.
The main thread then blocks until the new thread finishes.
The new thread executes in a loop, blocking for a moment and reporting progress. After five iterations of the loop, the new thread reports a final message and terminates.
The main thread notices that the new thread has finished and then terminates itself, closing the program.
1 2 3 4 5 6 |
Worker thread running... Worker thread running... Worker thread running... Worker thread running... Worker thread running... Worker closing down |
Next, let’s look at how we might update this example to stop the thread on demand.
Stop Custom Function in New Thread
We can update the example from the previous section to stop the new thread on demand.
This can be achieved by creating a new threading.Event instance, passing it as an argument to the new thread, and then updating the custom function to check if the event is set each iteration. Once the event is set, the function can break its task loop and exit, terminating the new thread.
Firstly, we can update the task function to take the shared threading.Event instance as an argument.
1 2 3 |
# custom task function def task(event): # ... |
We can then update the task loop to check the status of the event each iteration, and if set to break the task loop.
1 2 3 4 |
... # check for stop if event.is_set(): break |
Tying this together, the updated task() function with these changes is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 |
# custom task function def task(event): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # check for stop if event.is_set(): break # report a message print('Worker thread running...') print('Worker closing down') |
Next, in the main thread can first create a new threading.Event instance to be shared between the threads.
1 2 3 |
... # create the event event = Event() |
We can then update the main thread to block for a few seconds.
1 2 3 |
... # block for a while sleep(3) |
Finally, we can set the event and trigger the new thread to stop.
1 2 3 4 |
... # stop the worker thread print('Main stopping thread') event.set() |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# SuperFastPython.com # example of stopping a new thread from time import sleep from threading import Thread from threading import Event # custom task function def task(event): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # check for stop if event.is_set(): break # report a message print('Worker thread running...') print('Worker closing down') # create the event event = Event() # create and configure a new thread thread = Thread(target=task, args=(event,)) # start the new thread thread.start() # block for a while sleep(3) # stop the worker thread print('Main stopping thread') event.set() # wait for the new thread to finish thread.join() |
Running the example first creates and starts the new thread.
The main thread then blocks for a few seconds.
Meanwhile, the new thread executes its task loop, blocking and reporting a message each iteration. It checks the event each iteration, which remains false and does not trigger the thread to stop.
The main thread wakes up, and then sets the event. It then joins the new thread, waiting for it to terminate.
The task thread checks the event which is discovered to be set (e.g, True). The thread breaks the task loop, reports a final message then terminates the new thread.
The main thread then terminates, closing the Python process.
1 2 3 4 |
Worker thread running... Worker thread running... Main stopping thread Worker closing down |
Now that we know how to stop a custom function running in a new thread, let’s look at an example of stopping a custom thread class.
Free Python Threading Course
Download your FREE threading PDF cheat sheet and get BONUS access to my free 7-day crash course on the threading API.
Discover how to use the Python threading module including how to create and start new threads and how to use a mutex locks and semaphores
Example of Stopping a Thread With an Extended Class
In this section we can explore how to stop a thread that is an object that extends the threading.Thread class.
First, we will develop an example that extends the threading.Thread class, then we will look at how to update the example to stop the thread on demand.
Custom Thread Class
We can run a task in a new thread by extending the threading.Thread class and overriding the constructor and the run() function.
In this section, we will show how to run the same task as in the previous section within a custom thread class.
Firstly, we can define a new class that extends threading.Thread.
1 2 3 |
# custom thread class class CustomThread(Thread): # ... |
The new class must implement a constructor and call the constructor of the threading.Thread class to properly initialize the underlying thread instance.
1 2 3 4 |
# constructor def __init__(self): # call the parent constructor super(CustomThread, self).__init__() |
We can then override the run() method on the threading.Thread class.
This function will run in a new thread once the thread is started via the start() function. Specifically we call start() and start() will call run() for us in a new thread of execution.
Our task will be to execute a loop five times, block for a second to simulate work, and report a message. Then report a message once the task is complete.
1 2 3 4 5 6 7 8 9 |
# execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # report a message print('Worker thread running...') print('Worker closing down') |
Tying this together the new CustomThread class is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# custom thread class class CustomThread(Thread): # constructor def __init__(self): # call the parent constructor super(CustomThread, self).__init__() # execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # report a message print('Worker thread running...') print('Worker closing down') |
Next, in the main thread, we can create a new instance of our custom thread class and start it in order to execute our task in a new thread.
1 2 3 4 5 |
... # create a new thread thread = CustomThread() # start the new thread thread.start() |
We will then join() the new thread and wait for the new thread to terminate, before terminating the main thread.
1 2 3 |
... # wait for the new thread to finish thread.join() |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# SuperFastPython.com # example of a extending the thread class from time import sleep from threading import Thread # custom thread class class CustomThread(Thread): # constructor def __init__(self): # call the parent constructor super(CustomThread, self).__init__() # execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # report a message print('Worker thread running...') print('Worker closing down') # create a new thread thread = CustomThread() # start the new thread thread.start() # wait for the new thread to finish thread.join() |
Running the example first creates our custom thread class then starts executing the new thread.
The main thread then blocks waiting for the new thread to finish.
Our custom thread class runs our task in a new thread of execution, looping five times. Each iteration it blocks for a moment then reports a message.
Once the task is finished, it reports a final message.
The new thread terminates, then the main thread terminates, closing the process.
1 2 3 4 5 6 |
Worker thread running... Worker thread running... Worker thread running... Worker thread running... Worker thread running... Worker closing down |
Next, let’s look at how we might update this example to stop the task on demand.
Stop Custom Thread Class
We can update the example in the previous section to stop the thread on demand.
This can be achieved by sharing a threading.Event with the new thread and updating the run() function to check if the event is set each iteration. Once set, the run() function can exit, which will terminate the new thread.
Firstly, we can update the constructor of our new thread class to take the shared threading.Event as an argument and then store it as a member variable.
1 2 3 4 5 6 |
# constructor def __init__(self, event): # call the parent constructor super(CustomThread, self).__init__() # store the event self.event = event |
Next, we can update the run() function to check the status of the threading.Event each iteration and to break the task loop once the event is set.
1 2 3 4 5 6 7 8 9 10 11 12 |
# execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # check for stop if self.event.is_set(): break # report a message print('Worker thread running...') print('Worker closing down') |
Tying this together, the updated CustomThread class that can be stopped on demand is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# custom thread class class CustomThread(Thread): # constructor def __init__(self, event): # call the parent constructor super(CustomThread, self).__init__() # store the event self.event = event # execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # check for stop if self.event.is_set(): break # report a message print('Worker thread running...') print('Worker closing down') |
In the main thread, we must first create an instance of an event that can be shared between threads.
1 2 3 |
... # create the event event = Event() |
We must then pass it to the constructor of our new CustomThread class.
1 2 3 |
... # create a new thread thread = CustomThread(event) |
We can then update the new thread to block for a few seconds, then request that the new thread terminate by setting the event.
1 2 3 4 5 6 |
... # block for a while sleep(3) # stop the worker thread print('Main stopping thread') event.set() |
Finally, the main thread will wait for the new thread to terminate.
1 2 3 |
... # wait for the new thread to finish thread.join() |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# SuperFastPython.com # example of stopping a custom thread class from time import sleep from threading import Thread from threading import Event # custom thread class class CustomThread(Thread): # constructor def __init__(self, event): # call the parent constructor super(CustomThread, self).__init__() # store the event self.event = event # execute task def run(self): # execute a task in a loop for i in range(5): # block for a moment sleep(1) # check for stop if self.event.is_set(): break # report a message print('Worker thread running...') print('Worker closing down') # create the event event = Event() # create a new thread thread = CustomThread(event) # start the new thread thread.start() # block for a while sleep(3) # stop the worker thread print('Main stopping thread') event.set() # wait for the new thread to finish thread.join() |
Running the example first involves creating an instance of the custom thread and starting it.
The main thread will then block for a few seconds.
The new thread will start executing its task loop, blocking for a moment and reporting progress.
The main thread then wakes up and sets the event, triggering the new thread to stop.
The new thread notices that the event is set and then exits the task loop, reporting a final message and terminating the new thread.
The main thread waits for the new thread to terminate, before then stopping itself and closing the application.
1 2 3 4 |
Worker thread running... Worker thread running... Main stopping thread Worker closing down |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Common Questions About Stopping Threads
This section lists common questions about stopping a new thread in Python.
Do you have any questions about stopping a new thread? Let me know in the comments and I will do my best to help.
What if My Task Does Not Have a Loop?
Your task does not need to have a loop in order to be stopped.
Instead, you need to add frequent checks of the shared threading.Event and return from the task() function or run() function once the event is set.
This will depend on the specifics of the task you wish to exit in a new thread.
If your task involves blocking, such as reading or writing IO or waiting on another thread, you may want to adopt a busy wait and have the threading.Event checked each iteration.
You can learn more about implementing a busy wait here:
If your task remains idle for some time, such as waiting for sporadic events, it may be able to wait on the threading.Event itself via the wait() function.
What if My Task Raises An Exception?
If your task executing in a new thread raises an exception that is not handled, it will terminate the thread.
This could be an alternate way for you to trigger a new thread to terminate, if convenient.
What if My New Thread Has Already Stopped?
If the new thread has already stopped and you set the event, it will not have any effect.
It would not be a problem.
What if My New Thread is a Daemon Thread?
If your task is executing in a daemon thread, then there is no need to stop it manually as it will be terminated for you once all non-daemon threads (e.g. the main thread) terminate.
You can learn more about daemon threads here:
If you want to manually stop your daemon thread, you can use the same method as above.
You can see an example of this here:
How Can We Stop Multiple Threads?
You can share the same threading.Event between all of the threads that might want to stop.
Each thread can frequently check the status of the event.
Once set, all threads can exit their task and terminate.
How Can We Stop Daemon Threads?
The same technique of using a threading.Event can be used to stop daemon threads.
You can see an example of this here:
How Can We Stop Tasks in a ThreadPoolExecutor?
The same technique of using a threading.Event can be used to stop tasks executing in a ThreadPoolExecutor.
You can see an example of this here:
Further Reading
This section provides additional resources that you may find helpful.
Python Threading Books
- Python Threading Jump-Start, Jason Brownlee (my book!)
- Threading API Interview Questions
- Threading Module API Cheat Sheet
I also recommend specific chapters in the following books:
- Python Cookbook, David Beazley and Brian Jones, 2013.
- See: Chapter 12: Concurrency
- Effective Python, Brett Slatkin, 2019.
- See: Chapter 7: Concurrency and Parallelism
- Python in a Nutshell, Alex Martelli, et al., 2017.
- See: Chapter: 14: Threads and Processes
Guides
- Python Threading: The Complete Guide
- Python ThreadPoolExecutor: The Complete Guide
- Python ThreadPool: The Complete Guide
APIs
References
Takeaways
You now know how to stop a thread in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Leon Seierlein on Unsplash
Do you have any questions?