这个问题与这个有很大关系,后者没有解决方案,但并不完全相同。
我想问是否有一种方法可以在PyQt中启动后台任务,并且能够通过按按钮来
kill
它。
我的问题是我有一个用户界面和一些外部(第三方)函数,需要一段时间才能计算。为了在任务计算时不冻结用户界面,我使用
QThread
在后台运行它们,并在它们完成时使用 signals
同步 UI。
但是,我想添加外部用户按下按钮并取消当前任务的选项(因为不再需要/不需要该任务)。
对我来说,在 Linux 中看起来像
kill -9 *task*
一样简单的东西,在 Qt 中很难/很困难地获得。
现在我正在使用以下形式的自定义 Qthreads:
mythread = Mythread()
mythread.finished.connect(mycallback)
mythread.start()
其中
Mythread
继承 QThread 重写 run
方法。
在用户界面中,有一个按钮尝试通过以下任一方式终止该线程:
mythread.exit(0)
mythread.quit()
mythread.terminate()
它们都不起作用...我知道文档指出
terminate
方法确实有奇怪的行为...
所以问题是..我面对这个问题是错误的吗?如何杀死一个QThread?如果不可能,有什么替代方法吗?
谢谢!
尝试按照您建议的方式杀死
QThread
是一个非常常见的错误。这似乎是由于未能意识到需要停止的是长时间运行的任务,而不是线程本身。
该任务被移至工作线程,因为它阻塞了主/GUI 线程。但一旦任务转移,这种情况并没有真正改变。它将以与阻塞主线程完全相同的方式阻塞工作线程。为了使线程完成,任务本身必须正常完成,或者以某种方式以编程方式停止。也就是说,必须采取一些措施来允许线程的
run()
方法正常退出(这通常需要打破阻塞循环)。
取消长时间运行的任务的常见方法是通过简单的停止标志:
class Thread(QThread):
def stop(self):
self._flag = False
def run(self):
self._flag = True
for item in get_items():
process_item(item)
if not self._flag:
break
self._flag = False
我也面临着与
QThreads
同样的挑战。经过多次实验,我发现以下解决方案对于在 PyQt5 中管理线程非常有效:
关键是使用
requestInterruption
方法,该方法允许线程检查中断请求并以受控方式终止自身。代码如下:
from PyQt5.QtCore import QThread, pyqtSignal
class Worker(QThread):
def run(self):
while True:
if self.isInterruptionRequested():
break
# do time consuming job here
QThread.sleep(100) # to illustrate expensive task
class MainWindow:
# init and other mechanism
def kill_worker():
# Gently kills the thread
if self._thread.isRunning():
self._thread.requestInterruption() # kills straightforwardly
self._thread.quit()
self._thread.wait()
self._thread.deleteLater()