在我的代码(使用 Tkinter 的复杂 GUI 应用程序)中,我在自定义对象(进度条)中定义了一个线程。它运行一个带有 while cicle 的函数,如下所示:
def Start(self):
while self.is_active==True:
do it..
time.sleep(1)
do it..
time.sleep(1)
def Stop(self):
self.is_active=False
只有当放置在另一个线程中的另一段代码使用方法 self.Stop() 更改属性 self.is_active 时,它才能终止。我在另一个自定义对象(计数器)中遇到同样的情况,当另一个线程(主线程)工作时,它们都必须一起工作。
代码可以工作,但我意识到与进度条和计数器关联的两个线程不会像我想要的那样立即终止,因为在终止之前,它们需要等待其函数结束,而这些线程很慢,因为time.sleep(1) 指令。从用户的角度来看,这意味着看到主线程的结尾带有进度条和终止 LATE 的计数器,我不喜欢它。
说实话我不知道如何解决这个问题。有没有办法强制线程立即终止而不等待函数结束?
首先要明确的是,硬终止线程在任何语言中都是一个糟糕的主意,Python 不支持它;如果不出意外的话,该线程持有从未解锁的锁的风险,导致任何试图获取它的线程死锁,这是一个致命的缺陷。
如果你根本不关心线程,你可以用
daemon=True
参数创建它,如果进程中的所有非守护线程都退出了,它就会消亡。但是,如果线程确实应该通过适当的清理而终止(例如,它可能有 with
语句或类似的语句来管理进程外部资源的清理,但在进程终止时不会被清理),那么这不是真正的解决方案。
也就是说,您可以通过从使用普通
bool
和 time.sleep
切换到使用 Event
并使用 .wait
方法来避免等待一秒或更长时间。这将允许“睡眠”立即被中断,但需要付出很小的代价,要求您逆转您的条件(因为 Event.wait
仅在其为 false/未设置时才会阻塞,因此您需要基于何时应该停止的标志,不是当您当前处于活动状态时):
class Spam:
def __init__(self):
self.should_stop = threading.Event() # Create an unset event on init
def Start(self):
while not self.should_stop.is_set():
# do it..
if self.should_stop.wait(1):
break
# do it..
if self.should_stop.wait(1):
break
def Stop(self):
self.should_stop.set()
在现代 Python(3.1 及更高版本)上,如果事件已设置(在开始等待时或因为它在等待时设置),则
wait
方法返回 True
,否则返回 False
,因此每当 wait
返回 True
,这意味着您被告知停止,您可以立即break
退出循环。您几乎会立即收到通知,而不必等待一秒钟才能检查标志。
这不会导致真正的“do it..”代码立即退出,但从你所说的来看,这部分代码听起来并没有那么长,所以等待它完成并不是一个问题好麻烦。
如果您确实想保留
is_active
属性来测试它是否仍然处于活动状态,您可以将其定义为反转 Event
含义的属性,例如:
@property
def is_active(self):
return not self.should_stop.is_set()
在不冒分段错误风险的情况下,最安全的方法是返回。
def Start(self):
while self.is_active==True:
do it..
if not self.is_active: return
time.sleep(1)
if not self.is_active: return
do it..
if not self.is_active: return
time.sleep(1)
def Stop(self):
self.is_active=False
python 线程需要释放相关资源,虽然可以使用一些 C 技巧“杀死”线程,但您将面临分段错误或内存泄漏的风险。
这是一种更干净的方法。
class MyError(Exception):
pass
def Start(self):
try:
while self.is_active==True:
do it..
self.check_termination()
time.sleep(1)
self.check_termination()
do it..
self.check_termination()
time.sleep(1)
except MyError:
return
def check_termination(self):
if not self.is_active:
raise MyError
并且您可以从任何函数内部调用
self.check_termination()
来终止此循环,而不必直接从 Start
内部调用。
编辑:ShadowRanger 解决方案可以更好地处理“可中断等待”,我只是保留它来实现线程的终止开关,可以从线程内的任何位置进行检查。
我们可以使用
threading.Event()
包杀死没有 PyThreadKiller
的线程。
请按照给出的示例脚本的项目描述下载这些 PyPi 包。 我希望你期待这个答案。
https://pypi.org/project/PyThreadKiller/
pi
pip3.12 install PyThreadKiller