Python завершить все потоки

How to Close a Thread in Python

You can close a new thread by returning from run() or raising an exception.

In this tutorial you will discover how to close a thread in Python.

Need to Close 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:

While executing a function in a new thread we may need to stop the thread immediately.

This could be for many reasons, such as:

  • Based on a condition or state within the application.
  • A dependent resource is no longer available.
  • The user requests the program be closed.

How can we close a new thread immediately?

Run your loops using all CPUs, download my FREE book to learn how.

How to Close a New Thread

A new thread will close when the run() function of the threading.Thread class returns.

This can happen in one of two ways:

  • The run() function returns normally.
  • The run() function raises an uncaught error or exception.

We can return or raise an uncaught exception to close a thread, and this can be implemented in a few ways, such as:

  1. Call return from a target task function.
  2. Call system.exit().
  3. Raise an Error or Exception.

Let’s take a closer look at each.

Approach 1. Close Thread By Return

The run() function of the threading.Thread class will execute our target function in a new thread of execution.

Consider the case where we create a new thread and configure it to execute a custom task() function via the “target” argument. The thread is then started by calling the start() function.

In this case, the start() function executes the run() function of the threading.Thread class in a new thread and returns immediately.

The run() function of the threading.Thread class will call our task() function. Our task function will return eventually, then the run function will return and the thread will terminate.

This is the normal usage of a thread.

We can choose to close the thread any time from within our task function.

This can be achieved by returning from the task function.

This will terminate the thread.

The trigger to close the thread may come from another thread, such as a boolean variable or an event.

You can learn more about triggering a thread to stop from another thread in this tutorial:

Approach 2. Close Thread By sys.exit()

Another approach is to call the sys.exit() function at any point within our task() function or in the functions it calls.

This will raise a SystemExit exception which will not be caught and will terminate the new thread.

This approach is helpful if we are deep down the call-graph of custom functions and the return statement is not convenient.

Approach 3. Close Thread By Exception

Another approach is to raise an Error or Exception in the target function or any called function.

If the Error or Exception is uncaught it will unravel the call graph of the thread, then terminate the thread.

The downside of this approach is that the default handler for uncaught exceptions will report the exception to the terminal. This can be changed by specifying a handler via threading.excepthook.

You can learn more about unhandled exceptions in threads here:

Now that we know how to close a thread from within the thread, let’s look at some worked examples.

Confused by the threading module API?
Download my FREE PDF cheat sheet

Close Thread By Returning

We can close a thread by returning from the run function at any time.

This can be achieved by using the “return” statement in our target task function.

If the threading.Thread class has been extended and the run() function overridden, then the “return” statement can be used in the run() function directly.

We can demonstrate this with a worked example.

In this example, we will have a task that loops forever. Each iteration, it will generate a random number between 0 and 1, report the value then sleep for a fraction of a second. If the generated value is greater than 0.9, then the thread will choose to close immediately.

The task() function below implements this.

Источник

Python завершить все потоки

Let’s say that you want to start a thread and run some background processing there but also you want to stop the program when you press CTRL+C or set a time out. (if you want to close all threads scroll down to the end)

  1. Just close the thread and don’t care what happens
  2. Wait for the background thread to finish the current task and then return from it

Note

Daemon threads are abruptly stopped at shutdown. Their resources (such as open files, database transactions, etc.) may not be released properly. If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event . (from python documentation)

If we manage to close the main-thread all the daemon threads will be forced to close.

Using daemon threads for tasks that can be interrupted

1. Using try/except for KeyboardInterrupt

Notice that we don’t use join() , because is blocking, we can’t detect when CTRL+C has been pressed when we are blocked. Because of this we use is_alive() to check if the thread finished or not.

#!/usr/bin/env python3 import sys import threading from time import sleep def process(): """Long lasting operation""" sleep(300) print("finished work") def main(argv): t = threading.Thread(target=process) t.daemon = True t.start() try: while t.is_alive(): print("Waiting for background thread to finish") sleep(1) print("Thread finished task, exiting") except KeyboardInterrupt: print("Closing main-thread.This will also close the background thread because is set as daemon.") return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) 

2. Using signal.signal to exit main thread

SIGINT is the signal for CTRL+C and the signal_handler is the function to be executed when the SIGINT is detected.

#!/usr/bin/env python3 import signal import sys import threading from time import sleep def process(): """Long lasting operation""" sleep(300) print("finished work") def signal_handler(signal, frame): print("Closing main-thread.This will also close the background thread because is set as daemon.") sys.exit(0) def main(argv): signal.signal(signal.SIGINT, signal_handler) t = threading.Thread(target=process) t.daemon = True t.start() while t.is_alive(): print("Waiting for background thread to finish") sleep(1) print("Thread finished task, exiting") return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) 

3. Using join() to add a timeout and then close all threads (can’t catch CTRL+C)

The call to join() is blocking, we can’t catch CTRL+C, so we will see another example how to add timeout and catch CTRL+C. You can use this if in your context you don’t care about CTRL+C.

Don’t use join() on a daemon thread without timeout, by definition daemon threads are infinite loops but nobody stops you to mark a thread as daemon that is not an infinite loop.

#!/usr/bin/env python3 import sys import threading from time import sleep TIMEOUT = 10 def process(): """Long lasting operation""" sleep(12) print("finished work") def main(argv): """ This example has timeout using the join() method. The join() is blocking, meaning that an SIGINT event(CTRL+C) can't be detected. :param argv: :return: """ t = threading.Thread(target=process) t.daemon = True t.start() print(f"Waiting seconds for background thread to finish.") t.join(TIMEOUT) if t.is_alive(): print("Background thread timed out. Closing all threads") return -1 else: print("Background thread finished processing. Closing all threads") return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) 

4. Using is_alive() for timeout and catching CTRL+C

#!/usr/bin/env python3 import sys import threading from time import sleep TIMEOUT = 10 def process(): """Long lasting operation""" sleep(12) print("finished work") def main(argv): """ This example combines timeout and KeyboardInterrupt. If detected KeyboardInterrupt all threads are closed. The background thread is close automatically because is set as daemon. :param argv: :return: """ t = threading.Thread(target=process) t.daemon = True t.start() print(f"Waiting seconds for background thread to finish.") try: loop_thread(t) except KeyboardInterrupt: print("KeyboardInterrupt detected closing all threads") def loop_thread(t): i = 1 while i  TIMEOUT: if t.is_alive(): print("Background thread processing, please wait.") sleep(1) i += 1 else: print("Background thread finished processing. Closing all threads") if i == TIMEOUT + 1: print("Timeout occurred.") if __name__ == "__main__": sys.exit(main(sys.argv)) 

Closing an non daemon thread gracefully (waiting for thread to finish work)

1. Using Event() to stop the thread gracefully

As stated in the python doc, this example uses an Event() which will be set when CTRL+C is pressed. The background thread will check this flag every time it gets a new item to process, if set will not get any new items and just return.

This thread is not marked as daemon meaning that the main-thread can’t close if this thread hasn’t finished executing.

If we do one time background job we can just have the code in the thread target function, if we want the thread to loop over some items we can use a queue.Queue()

#!/usr/bin/env python3 import queue import sys import threading from time import sleep stop = threading.Event() work_queue = queue.Queue() work_queue.put('Item 1') work_queue.put('Item 2') def process(): """Long lasting operation""" while not stop.isSet() and not work_queue.empty(): sleep(5) item = work_queue.get() # process item print(f"Done task: ") work_queue.task_done() if stop.isSet(): print("KeyboardInterrupt detected, closing background thread. ") def main(argv): t = threading.Thread(target=process) t.start() try: while t.is_alive(): print("Waiting for background thread to finish") sleep(1) except KeyboardInterrupt: stop.set() print("Closing main-thread.Please wait for background thread to finish the current item.") return 0 work_queue.join() print("Thread finished all tasks, exiting") return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) 

Forcefully closing all threads os._exit(), even those that are not a daemon thread

#!/usr/bin/env python3 import os import queue import sys import threading from time import sleep work_queue = queue.Queue() work_queue.put('Item 1') work_queue.put('Item 2') def process(): """Long lasting operation""" while not work_queue.empty(): sleep(5) item = work_queue.get() # process item print(f"Done task: ") work_queue.task_done() def main(argv): t = threading.Thread(target=process) t.start() try: while t.is_alive(): print("Waiting for background thread to finish") sleep(1) except KeyboardInterrupt: print("Closing main-thread.Please wait for background thread to finish the current item.") if threading.activeCount() > 1: os._exit(getattr(os, "_exitcode", 0)) else: sys.exit(getattr(os, "_exitcode", 0)) work_queue.join() print("Thread finished all tasks, exiting") return 0 if __name__ == "__main__": sys.exit(main(sys.argv)) 

You could also look into concurrent.futures depending on the situation the code could be easier to write using concurrent.futures.

Consider use of timeout with threads all the time in production , if code shouldn’t take more than 10 seconds use timeout, is always better to see an error in a log file that threaded timed out than having the issue hidden for probably hours and then you start debugging, there is no need to lose time and money.

When to use threads? When you wait on I/O. If you do cpu intensive work look into subprocess module

If you want to learn more about threads and concurrency in python you can watch this youtube video by python core developer Raymond Hettinger.

Источник

Читайте также:  React query typescript example
Оцените статью