【PyQt5】遇到QThread:Destroyed while thread is still running 解决方法

PyQt5在执行多线程的时候遇到:QThread:Destroyed while thread is still running

错误分析:

QThread的子线程还在运行但是线程就被销毁了,导致程序闪崩,该错误是笔者用终端执行py文件时提示的。

解决方法:

  1. 方法一:
  • 子线程是在后台不断的检测PC连接的设备,当检测到合适的设备连接成功后,主线程进行逻辑处理,此时子线程函数内部会直接return,等到主线程处理完连接设备的数据后,会再次新起一个子线程。(这里是因为主线程处理一次数据时间比较长,子线程没必要一直运行。)

注: 此处之所以不用互斥量来加锁,直接让子线程阻塞是因为担心死锁,而且本身程序比较小,耗用内存和CPU什么的都非常小,重启一个子线程也不会有影响。

  • 问题:
    但是子线程函数内部会直接return并不代表线程结束(归根结底还是自己不了解如何调用),所以直接再次调用self.thread_detec_l()会报错
    QThread: Destroyed while thread is still running。 解决方法:

再次调用self.thread_detec_l()之前添加两行:

python 复制代码
self.thread_detec_l = ObjectDetection()
self.thread_detec_l.quit()
self.thread_detec_l.wait()
self.thread_detec_l.start()
  1. 方法二:
    将调用线程的局部变量转换为整个主线程的实例变量
python 复制代码
def ObjectDetection_tf2(self)
    self.thread_detec_l = ObjectDetection()
    self.thread_detec_l.finished.connect(self.thread_detec_l.deleteLater)
    self.thread_detec_l.start()
  1. 方法三:
  • 虽然通过让线程中的局部变量转化为整个主线程的实例变量,从而避免了子线程在运行中就被销毁的问题,但是这种方式又会引起新问题而且比较隐蔽。当运行函数,子程序结束后,再次运行函数没有问题。但是当函数运行后子线程还在进行中,若此时再次运行就会重新初始化实例变量,导致之前运行的子线程又被中途销毁的问题出现,程序闪崩。

因此,好的解决方式是直接初始化一个实例列表变量self.task_list,每次初始化子线程后将临时变量加入到实例变量中这样也能实现变量随着程序长时间存在。

  • 在子线程结束后,发送一个完成的信号,获取信号发送对象sender,然后判断对象是否在self.task_list中,如果在的话就移除对象从而达到子线程完成后主动销毁线程解除内存占用。
python 复制代码
def ObjectDetection_tf2(self)
    thread_detec_l = ObjectDetection()
    self.task_list.append(thread_detec_l)
    thread_detec_l.finished.connect(self.remove_thread)
    thread_detec_l.start()
def remove_thread(self):
    sender = self.sender()
    if sender in self.task_list:
        try:
            self.task_list.remove(sender)
        except Exception as e:
            print(e)

笔者的具体情况:在主线程同时调用三个子线程进行后台的运算,在程序运行短暂时间就出现了如上错误。笔者的解决方法如下:

python 复制代码
def ObjectDetection_tf2(self):  # 开始处理
    if self.dir_path=='':  # 为左图所在的路径
        self.StateInfo.setText('请选择要进行检测的图像路径!')
        return
    if self.T==0:
        self.StateInfo.setText('请设置阈值!')
        return
    self.thread_detec_l = ObjectDetection(0,self.T,self.dir_path,self.tf2result_path)  # 子线程一
    self.task_list.append(self.thread_detec_l)
    self.thread_detec_l.finished.connect(self.remove_thread)
    self.thread_detec_l.results_img.connect(self.Hub_ResultsDisplay)
    self.thread_detec_l.report_info.connect(self.Hub_Reportinfo)
    self.thread_detec_l.process.connect(self.StateInfo.setText)  # 显示识别进度
    self.thread_detec_l.quit()
    self.thread_detec_l.wait()
    self.thread_detec_l.start()
    self.thread_detec_r = ObjectDetection(1,self.T,self.dir_path.replace('_L','_R'),self.tf2result_path)  # 子线程二
    self.task_list.append(self.thread_detec_r)
    self.thread_detec_r.finished.connect(self.remove_thread)
    self.thread_detec_r.results_img.connect(self.Hub_ResultsDisplay)
    self.thread_detec_r.report_info.connect(self.Hub_Reportinfo)
    self.thread_detec_r.quit()
    self.thread_detec_r.wait()
    self.thread_detec_r.start()
def Reportinfo(self,Hub_reportinfo_l,Hub_reportinfo_r):  # 中转站识别报告信息左右合并
    self.dict_l = Hub_reportinfo_l[self.index_reportinfo]
    self.dict_r = Hub_reportinfo_r[self.index_reportinfo]
    for img_name,report_dict in self.dict_l.items():  # 字典只有一组元素也得用for来分离键和值
        print(img_name,report_dict)
        if img_name in self.dict_r:
            self.dict_l[img_name].update(self.dict_r[img_name])   # 以{'K239+001-00000001':{(y1,x1,y2,x2):'Hengfeng',.....}}
            self.thread_perimg_count = Perimg_Count(self.dict_l)  # 子线程三
            self.task_list.append(self.thread_perimg_count)
            self.thread_perimg_count.finished.connect(self.remove_thread)
            self.thread_perimg_count.excel_info.connect(self.InfoExcel)  # 写入明细栏
            self.thread_perimg_count.quit()
            self.thread_perimg_count.wait()
            self.thread_perimg_count.start()
            self.index_reportinfo += 1
            # self.thread_perimg_count.exec()

def remove_thread(self):
    sender = self.sender()
    if sender in self.task_list:
        try:
            self.task_list.remove(sender)
        except Exception as e:
            print(e)

笔者结合三种方法,成功实现了三个子线程同时运行,而不出现某个子线程中途被销毁的情况。

相关推荐
databook11 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar12 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户83562907805112 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_12 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机19 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机20 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机20 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机20 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i20 小时前
drf初步梳理
python·django
每日AI新事件20 小时前
python的异步函数
python