PyQt5 线程管理 解决耗时线程导致假死问题

前言

​ 由于项目需求,需要设计一款目标检测后进行后台控制,并在界面上实时显示的上位机。设计的一开始没有考虑到上位机后台功能实现的耗时,功能实现后发现执行某些耗时操作会导致程序界面无响应,进入假死状态,最终采用线程管理解决这一问题。

简介

​ PyQt5是Digia的一套Qt5应用框架与python的结合,同时支持python2.x和python3.x。PyQt5由一系列python模块组成,适用于Linux,Windows,Mac OS等主流操作系统。下面简单介绍其安装的方式:

pip install PyQt5 
pip install PyQt5-tools

​ Python 中,有关线程开发的部分被单独封装到了模块中,threading是Python3之后的线程模块,提供了功能丰富的多线程支持。

threading主要通过两种方式来创建线程:

  • 使用Thread 类的构造器创建线程。即直接对类 threading.Thread 进行实例化创建线程,并调用实例化对象的 start() 方法启动线程。
  • 继承Thread 类创建线程类。即用 threading.Thread 派生出一个新的子类,将新建类实例化创建线程,并调用其 start() 方法启动线程。

假死状态

​ 通常我们使用应用时,总是希望其在运行后台操作的同时,又能够在最快的时间内响应用户的操作,这也是市场上大多数应用能够做到的。而当我们自己作为初学者在设计应用时常常容易将注意力集中在功能的实现从而忽略关于了所实现的功能与应用的耦合关系。

​ 这就会造成一个严重的问题,当用户的某个操作需要比较久的计算时间时,由于程序是单线程的设计,界面会呈现出无响应状态,以至于用户认为程序停止运行,降低使用的体验感。这类问题我们也称为应用的假死状态。随着应用功能越来越复杂,高效解决这类问题十分有必要!

解决方法

​ 通过python中的threading库,可以将耗时线程转入后台,避免其操作堵塞主线程,造成应用进入假死状态。同时还要注意,多线程中涉及到的内存的安全问题,可以使用互斥锁解决。

import threading

def task(a, b):
    ···

thread = threading.Thread(target=task, args=(a, b))
# 把子进程设置为守护线程,必须在start()之前设置
thread.setDaemon(True)  
# 开始执行线程
thread.start()

​ 由于项目需求,耗时线程中含有对视频流的获取和目标检测操作。Opencv的VideoCapture对视频流的读取并不是实时的,尤其是获取间隔时间较长时,会造成帧缓冲区的堆积,并且不能确定Opencv会在何时清理该缓冲区。所以,直接读取不仅会造成程序假死,缓冲区的堆积也会使帧的延迟严重。

​ 这里的解决方法是,将视频流的读取和处理分成两个线程,一个线程不断地读取视频流中的帧缓冲区,另一线程执行处理操作。

import threading
from threading import Lock
import multiprocessing as mp
import cv2

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))

queues1 = mp.Queue(maxsize=4)

def cap_put(self, queue):
    while True:
        queue.put(cap.read()[1])
        queue.get() if queue.qsize() > 1 else queue
        
def cap_get(self, queue):
    while True:
        img = queue.get()
        cv2.imshow("frame", img)
        cv2.waitKey(30)
            
put_thread = threading.Thread(target=cap_put, args=(queue))
# 把子进程设置为守护线程,必须在start()之前设置
put_thread.setDaemon(True)  
put_thread.start()

get_thread = threading.Thread(target=cap_get, args=(queue))
get_thread.setDaemon(True)  # 把子进程设置为守护线程,必须在start()之前设置
get_thread.start()

​ 如果考虑多线程之间的安全问题,也可以为自己的线程操作上锁,python threading提供了方便的互斥锁操作,如下所示

from threading import Lock

lock1 = Lock()
#加锁
lock1.acquire()

try :
    #需要保证线程安全的代码
    #...
    #使用finally 块来保证释放锁
finally :
    #修改完成,释放锁
    self.lock.release()

参考

Python多线程库threading的使用

Python内置库:threading(多线程)

后续

 喜欢的话可以关注一下我的公众号技术开发小圈,尤其是对深度学习以及计算机视觉有兴趣的朋友,我会把相关的源码以及更多资料发在上面,希望可以帮助到新入门的大家!
在这里插入图片描述

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
PyQt5是一个基于Qt框架的Python库,用于创建GUI应用程序。尽管PyQt5提供了多线程的支持,但在某些情况下,仍然可能出现假死现象。 首先,假死的原因可能是因为多线程之间的资源竞争。当多个线程同时访问共享资源时,可能会导致数据竞争和冲突,从而导致程序无法继续执行。为了解决这个问题,可以使用锁(Locks)或者互斥量(Mutex)来确保在任意时刻只有一个线程可以访问共享资源。 其次,假死也可能是由于多线程中的死锁情况引起的。死锁通常发生在多个线程同时等待彼此持有的资源,从而导致程序无法继续前进。为了避免死锁,可以采用避免竞争的设计原则,如避免使用多个锁,或使用超时或定时机制避免长时间等待。 此外,假死还可能是由于线程调度导致的。在某些情况下,系统可能会长时间地调度某个线程导致其他线程无法得到执行机会,从而看起来像是假死。为了解决这个问题,可以使用线程优先级或调度算法来平衡各个线程的执行。 最后,假死还可能是由于资源耗尽或内存泄漏导致的。在使用多线程时,需要合理地管理资源和内存消耗,避免资源耗尽或者长时间运行导致内存泄漏。 总结起来,解决PyQt5多线程假死问题的关键在于合理设计和管理多线程的资源竞争、避免死锁、调整线程的调度和优先级,并且合理管理资源和内存消耗。通过以上的措施可以有效地避免或解决PyQt5多线程假死问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rosen.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值