请考虑以下代码:
#! /usr/bin/env python3
import threading
import time
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
self._quit_flag = False
def run(self):
while not self._quit_flag:
print("Thread is alive!")
time.sleep(0.500)
def request_quit(self):
self._quit_flag = True
mt = MyThread()
mt.start()
将其保存为'test.py'并运行'python3 -i test.py'后,我得到一个交互式会话,其中线程定期打印“Thread is alive!”消息。
当我按下control-D或运行exit()时,python3进程不会终止,因为该线程仍在运行。我想解决这个问题。
然而,我不想让线程成为守护线程 - 我希望能够正确地结束/加入线程,因为在我正在解决的现实案例中,我想很好地终止网络连接。
有办法做到这一点吗?例如,在REPL循环终止之后,就在主线程退出之前执行了一些可用的挂钩?
答案 0 :(得分:2)
好的,我找到了自己做的方法。
查看Python源代码,结果发现在关闭时,Python会尝试在完成关闭进程之前加入()所有非守护程序线程。哪种方式有意义,除非如果有记录的方式来表示这些线程,这将更有用。没有。
然而, 可以重新实现从Thread派生的一个人自己的类的join()方法,这可以作为一种通知子线程它应该自己关闭的方法下:
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
self._quit_flag = False
def __del__(self):
print("Bye bye!")
def run(self):
while not self._quit_flag:
print("Thread is alive!", threading.active_count(), threading.main_thread().is_alive())
for t in threading.enumerate():
print(t.is_alive())
time.sleep(0.500)
def request_quit(self):
self._quit_flag = True
def join(self):
self.request_quit()
super().join()
然而,解决这个问题的方法实际上是一个黑客攻击,它忽略了'threading'模块docs(https://docs.python.org/3/library/threading.html)中的以下段落:
然而,它确实很有效。Thread类表示在单独运行的活动 控制线程。有两种方法可以指定活动:by 将可调用对象传递给构造函数,或者通过覆盖 子类中的run()方法。 没有其他方法(除了 构造函数)应该在子类中重写。换句话说,只有 覆盖此类的__init __()和run()方法。
答案 1 :(得分:1)
我不确定这是否可以接受,但我能看到你的问题的唯一方法是从你的脚本而不是-i
标志启动交互式控制台。然后,在终止交互式会话后,您将能够继续执行退出程序:
import threading
import time
from code import InteractiveConsole
def exit_gracefully():
print("exiting gracefully now")
global mt
mt.request_quit()
mt.join()
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
self._quit_flag = False
def run(self):
while not self._quit_flag:
print("Thread is alive!")
time.sleep(0.500)
def request_quit(self):
self._quit_flag = True
mt = MyThread()
mt.start()
InteractiveConsole(locals=locals()).interact()
exit_gracefully()
这将在没有-i
标志的情况下执行,仅作为
python3 test.py
它仍然会像python -i
那样为您提供交互式控制台,但现在脚本在交互式shell退出后执行exit_gracefully()
。
答案 2 :(得分:1)
我认为你可以这样做的方法是使用atexit
注册一个清理函数,并将你的线程设置为一个守护进程,例如:
#! /usr/bin/env python3
import threading
import time
import atexit
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
# this ensures that atexit gets called.
self.daemon = True
self._quit_flag = False
def run(self):
while not self._quit_flag:
print("Thread is alive!")
time.sleep(0.500)
print("cleanup can also go here")
def request_quit(self):
print("cleanup can go here")
self._quit_flag = True
mt = MyThread()
def cleanup():
mt.request_quit()
mt.join()
print("even more cleanup here, so many options!")
atexit.register(cleanup)
mt.start()