Я играю с потоками на python 3.7.4 и хочу использовать atexit
для регистрации функции очистки, которая (чисто) завершит потоки.
Например:
# example.py
import threading
import queue
import atexit
import sys
Terminate = object()
class Worker(threading.Thread):
def __init__(self):
super().__init__()
self.queue = queue.Queue()
def send_message(self, m):
self.queue.put_nowait(m)
def run(self):
while True:
m = self.queue.get()
if m is Terminate:
break
else:
print("Received message: ", m)
def shutdown_threads(threads):
for t in threads:
print(f"Terminating thread {t}")
t.send_message(Terminate)
for t in threads:
print(f"Joining on thread {t}")
t.join()
else:
print("All threads terminated")
if __name__ == "__main__":
threads = [
Worker()
for _ in range(5)
]
atexit.register(shutdown_threads, threads)
for t in threads:
t.start()
for t in threads:
t.send_message("Hello")
#t.send_message(Terminate)
sys.exit(0)
Однако кажется, что взаимодействие с потоками и очередями в обратном вызове atexit
создает взаимоблокировку с некоторой внутренней процедурой завершения работы:
$ python example.py
Received message: Hello
Received message: Hello
Received message: Hello
Received message: Hello
Received message: Hello
^CException ignored in: <module 'threading' from '/usr/lib64/python3.7/threading.py'>
Traceback (most recent call last):
File "/usr/lib64/python3.7/threading.py", line 1308, in _shutdown
lock.acquire()
KeyboardInterrupt
Terminating thread <Worker(Thread-1, started 140612492904192)>
Terminating thread <Worker(Thread-2, started 140612484511488)>
Terminating thread <Worker(Thread-3, started 140612476118784)>
Terminating thread <Worker(Thread-4, started 140612263212800)>
Terminating thread <Worker(Thread-5, started 140612254820096)>
Joining on thread <Worker(Thread-1, stopped 140612492904192)>
Joining on thread <Worker(Thread-2, stopped 140612484511488)>
Joining on thread <Worker(Thread-3, stopped 140612476118784)>
Joining on thread <Worker(Thread-4, stopped 140612263212800)>
Joining on thread <Worker(Thread-5, stopped 140612254820096)>
All threads terminated
(KeyboardInterrupt
- это я использую ctrl-c
, так как процесс, кажется, зависает на неопределенный срок).
Однако, если я отправлю сообщение Terminate
перед выходом (раскомментируйте строку после t.send_message("Hello")
), программа не зависнет и корректно завершится:
$ python example.py
Received message: Hello
Received message: Hello
Received message: Hello
Received message: Hello
Received message: Hello
Terminating thread <Worker(Thread-1, stopped 140516051592960)>
Terminating thread <Worker(Thread-2, stopped 140516043200256)>
Terminating thread <Worker(Thread-3, stopped 140515961992960)>
Terminating thread <Worker(Thread-4, stopped 140515953600256)>
Terminating thread <Worker(Thread-5, stopped 140515945207552)>
Joining on thread <Worker(Thread-1, stopped 140516051592960)>
Joining on thread <Worker(Thread-2, stopped 140516043200256)>
Joining on thread <Worker(Thread-3, stopped 140515961992960)>
Joining on thread <Worker(Thread-4, stopped 140515953600256)>
Joining on thread <Worker(Thread-5, stopped 140515945207552)>
All threads terminated
Возникает вопрос: когда выполняется эта процедура threading._shutdown
относительно обработчиков atexit
? Имеет ли смысл взаимодействовать с потоками в обработчиках atexit
?
#t.send_message(Terminate)
? - person stovfl   schedule 20.11.2019atexit
до тех пор, пока не завершатся все потоки, не являющиеся демонами, что подозрительно похоже на ошибку, исправленную в Python 2.6.5 (см. - stackoverflow.com/questions/3713360/ и bugs.python.org/issue1722344). Обходной путь может состоять в том, чтобы обернуть основной код вtry
/finally
и вручную вызватьshutdown_threads(threads)
самостоятельно. - person martineau   schedule 20.11.2019