Created
March 19, 2015 04:32
-
-
Save clchiou/f2608cbe54403edb0b13 to your computer and use it in GitHub Desktop.
Python ThreadPoolExecutor (non-)graceful shutdown
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import concurrent.futures.thread | |
import sys | |
import time | |
from concurrent.futures import ThreadPoolExecutor, as_completed | |
def remove_file(path): | |
print('Removing file %s' % path) | |
time.sleep(10) # Pretending that I'm removing the file... | |
print('%s is removed' % path) | |
not_graceful = sys.argv[1:] and sys.argv[1] == '--not-graceful' | |
if not_graceful: | |
print('I will _not_ be shut down gracefully...') | |
else: | |
print('I will be shut down gracefully... (default behavior)') | |
with ThreadPoolExecutor(1) as executor: | |
futures = [executor.submit(remove_file, path) for path in 'abcd'] | |
try: | |
for future in as_completed(futures): | |
future.result() | |
except KeyboardInterrupt: | |
if not_graceful: | |
executor._threads.clear() | |
concurrent.futures.thread._threads_queues.clear() | |
raise |
@clchiou, thanks for you late reply :-)
In Python >= 3.7 the following part will return an error:
except KeyboardInterrupt:
if not_graceful:
executor._threads.clear()
concurrent.futures.thread._threads_queues.clear()
raise
For example, in my script
Traceback (most recent call last):
File "mssql_backup_threaded.py", line 196, in <module>
concurrent.futures.thread._threads_queues.clear()
File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\lib\concurrent\futures\__init__.py", line 53, in __getattr__
raise AttributeError(f"module {__name__} has no attribute {name}")
AttributeError: module concurrent.futures has no attribute thread
In order to workaround this issue you can add the following line to the importing part of your program:
from concurrent.futures import thread
Thanks for sharing this. Works perfectly as I wanted. ❤️
clear for both executor._threads.clear() and oncurrent.futures.thread._threads_queues.clear() do not exist anymore :(
I am running python 3.12.3
I think this is due to the change that made worker threads no longer daemon threads.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks.
A very late reply to @gaborbernat: The thread executor module joins the executor threads at two levels. One is in the shutdown function. I disabled it with
executor._threads.clear()
. Althoughshutdown(wait=False)
has the same effect, sinceexecutor.__exit__
waits unconditionally, the process will still be blocked at the end of thewith
block. That's why I used theexecutor._threads.clear()
. But if you are not using executor's context manager,shutdown(wait=False)
should just work.The second join point is the atexit callback. I think it can only be disabled with
concurrent.futures.thread._threads_queues.clear()
. (Note that this disables joining of threads of all executors.)A side note: I haven't dug into the revision history, but it is interesting that the executor module tries very hard to join the threads, and yet it sets all threads to be daemons.