pyqt与threading结合,概率性导致程序闪退
前因
为了方便调试设备,打算用python写个界面程序,可以实时观察设备的升级过程,后面考虑允许多个设备同时OTA升级,所以打算给每个设备的升级开一个线程。
遇到的问题
给单个设备升级的时候,概率性出现以下无提示报错:
Process finished with exit code -1073741819 (0xC0000005)
排故过程
1. 代码
我一度怀疑代码有问题,但后面基本排除了,因为闪退的时候程序的运行位置是不确定的,并且有时代码可以跑好几轮.
2. 多线程共享变量
我的代码思路大概如下
全局变量存储ui指针
主线程:创建ui,并将地址存在全部变量里
子线程1:创建Http服务器,并实时监听客户端
子线程2-n:为链接的设备创建ota任务,并在界面显示实时进度
所以存在子线程需要同时操作界面,可能导致异常的发生。
因此我创建了两个简单的线程:
def function(x: int, y):
while True:
x += 2
if True:
print(threading.currentThread().name)
win.ui.pte_serderInfo.appendPlainText(threading.currentThread().name)
else:
print(threading.currentThread().name + " {}".format("False"))
sleep(0.1)
value1 = 1
t = threading.Thread(target=function, args=(value1, value1))
t.start()
value2 = 2
t1 = threading.Thread(target=function, args=(value2, value2))
t1.start()
win是界面地址,发现如果不操作界面,两个线程不会有问题。
但如果一直操作界面,那将会大概率出现0xC0000005。
但我将于其中一个线程关闭,依然会出现标题所说的问题。
所以我遇到的问题并非第二种
3.兼容性
永远想不到的是我遇到的问题是兼容性问题。
其实通过度娘查询,发现有不少人说是第三方包版本兼容问题。
最后偶然把pyqt6改为pyqt5,问题一下就不见了。
后续
后面发现pyqt5的progressBar和threading也不兼容,我尝试采用以下方法规避:
1.线程通过操作全局变量,间接控制progressBar(成功)
子线程改变全局变量,主线程循环查询该变量
2.线程通过操作全局变量,通过主线程函数直接控制progressBar(失败)
将主线程的函数传出到全局变量,子线程进行操作(失败)
3.线程间通过队列传值(成功)
使用队列安全一些。
目前在主线程是使用QTimer进行接收队列。
经过上面的优化后,惊奇的发现把pyqt5改为pyqt6,也不会出现概率性闪退了。。。
自我反省
- 我所遇到的应该是“在子线程中操作主界面”和“第三方模块的兼容性”的问题。
- python虽然允许子线程操作pyqt主线程的变量或函数,但存在概率性程序崩溃的问题。
- 子线程想调用主线程的ui,需要间接控制,可以使用全局变量,队列等手段。
- 0xC0000005概率出现,大概率就是子线程直接操作了主线程的ui。
留下这篇文章主要是为了记录自己的错误,避免后面再次踩坑,再则可以提供一些思路给路过的码友们~