In this example, Tkinter GUI starts ThreadPoolExecutor. But ThreadPoolExecutor is inside a threading.Thread function. The thread function says it's finished
before ThreadPoolExecutor has started ... and returns this error.
RuntimeError: can't register atexit after shutdown
Here is the code:
import time
import threading
import tkinter as tk
import concurrent.futures
import random
def thread1(_details, lock):
print('started ' + _details[2])
time.sleep(random.randint(1, 10))
print('finished ' + _details[2])
return _details
def workpool():
file_lock = threading.Lock()
list1 = [['0', 'pending', 'name: test1'], ['1', 'pending', 'name: test2'], ['2', 'pending', 'name: test3'], ['4', 'pending', 'name: test4'], ['7', 'pending', 'name: test5'], ['8', 'pending', 'name: test6']]
print('thread running')
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
working_threads = {executor.submit(thread1, _details, file_lock): _details for index1, _details in enumerate(list1)}
for future in concurrent.futures.as_completed(working_threads):
current_result = future.result()
print('threads done')
def launch_threads():
workpool()
def new_button():
threading.Thread(name='Launch_ThreadPoolExecutor', target=launch_threads).start()
def main_thread():
root = tk.Tk()
root.geometry("+{}+{}".format(650, 50))
root.geometry("{}x{}".format(200, 200))
new_bt = tk.Button(root, text="New", command=new_button, height=2, padx=10, pady=5, width=15, wraplength=100)
new_bt.place(x=40, y=40, height=30, width=80)
root.mainloop()
threading.Thread(name='GUI_thread', target=main_thread).start()
Here is the full traceback:
Exception in thread Launch_ThreadPoolExecutor:
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\threading.py", line 1041, in _bootstrap_inner
self.run()
~~~~~~~~^^
File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\threading.py", line 992, in run
self._target(*self._args, **self._kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\GOOD\Coding\.Coding_Projects\test3.py", line 24, in workpool
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\concurrent\futures\__init__.py", line 50, in __getattr__
from .thread import ThreadPoolExecutor as te
File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\concurrent\futures\thread.py", line 37, in <module>
threading._register_atexit(_python_exit)
~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
File "C:\Users\User\AppData\Local\Programs\Python\Python313\Lib\threading.py", line 1503, in _register_atexit
raise RuntimeError("can't register atexit after shutdown")
RuntimeError: can't register atexit after shutdown
Tested only on Linux.
Code works correctly for me when I use .join() for first thread GUI_thread
p = threading.Thread(name="GUI_thread", target=main_thread)
p.start()
p.join()
Code works correctly also if I run directly
main_thread()
I don't think it needs to keep all GUI in thread.
Only code executed by button may need to use thread because only this code may freeze GUI.
Full working code
import time
import threading
import tkinter as tk
import concurrent.futures
import random
def thread1(_details, lock):
print("started " + _details[2])
time.sleep(random.randint(1, 10))
print("finished " + _details[2])
return _details
def workpool():
file_lock = threading.Lock()
list1 = [
["0", "pending", "name: test1"],
["1", "pending", "name: test2"],
["2", "pending", "name: test3"],
["4", "pending", "name: test4"],
["7", "pending", "name: test5"],
["8", "pending", "name: test6"],
]
print("thread running")
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
working_threads = {
executor.submit(thread1, _details, file_lock): _details
for index1, _details in enumerate(list1)
}
for future in concurrent.futures.as_completed(working_threads):
current_result = future.result()
print("threads done")
def launch_threads():
workpool()
def new_button():
threading.Thread(name="Launch_ThreadPoolExecutor", target=launch_threads).start()
def main_thread():
root = tk.Tk()
root.geometry("+{}+{}".format(650, 50))
root.geometry("{}x{}".format(200, 200))
new_bt = tk.Button(root, text="New", command=new_button, height=2, padx=10, pady=5, width=15, wraplength=100)
new_bt.place(x=40, y=40, height=30, width=80)
root.mainloop()
# --- only changes are here ---
# version 1 - works correctly on Linux
p = threading.Thread(name="GUI_thread", target=main_thread)
p.start()
p.join()
# version 2 - works correctly on Linux
#main_thread()