【PyQt5】| 多线程设计模式

两种多线程设计模式

继承QThread方式:

将线程对象和工作对象合二为一,工作逻辑在run()方法中实现。

  • 继承自QThread,并重写run()方法,在run()方法中执行耗时操作。
  • 线程启动时,run()方法被调用,线程结束,run()方法返回。
  • 这种方式将线程和任务绑定在一起,每个线程对象只能执行一个任务(run方法中定义的任务)。

Worker + moveToThread()方式

将线程对象(QThread)和工作对象(Worker)分离,工作逻辑在Worker的槽函数中实现。

  • 工作对象(Worker)继承自QObject,包含一些槽函数,这些槽函数将在工作线程中执行。

  • 创建一个QThread对象,并将Worker对象通过moveToThread()方法移动到新线程中。

  • 通过信号触发Worker的槽函数,从而在Worker所在线程中执行任务。

  • 这种方式将线程和任务分离,一个Worker对象可以在不同的线程中移动,并且一个线程可以供多个Worker对象使用(但同一时间只能有一个Worker对象在该线程中)。

    继承QThread方式
    class MyThread(QThread):
    def init(self, ...):
    super().init()
    # 初始化

    复制代码
      def run(self):
          # 耗时操作
          pass

    Worker + moveToThread()方式
    class Worker(QObject):
    def init(self, ...):
    super().init()
    # 初始化

    复制代码
      @pyqtSlot()
      def do_work(self):
          # 耗时操作
          pass

    使用方式

    thread = QThread()
    worker = Worker()
    worker.moveToThread(thread)
    thread.started.connect(worker.do_work)
    thread.start()

信号与槽的连接

在两种方式中,都可以使用信号和槽进行通信,但连接方式有所不同。

继承QThread方式

信号可以定义在QThread子类中,然后在run()方法中发射。

注意:在run()方法中发射信号时,这些信号是在工作线程的上下文中发射的,因此连接到这个信号的槽函数将在接收者所在的线程中执行(由连接类型决定)。

Worker + moveToThread()方式

信号定义在Worker对象中,然后在Worker的槽函数中发射。

由于Worker对象被移动到了工作线程,所以它的槽函数会在工作线程中执行,而信号发射也是在Worker对象所在的线程中。

  1. 线程的生命周期管理
    继承QThread方式
    线程对象启动后,run()方法执行,直到返回,线程结束。
    线程结束后,可以通过wait()等待线程结束,然后删除线程对象。
    Worker + moveToThread()方式
    线程对象启动后,通过信号触发Worker的槽函数,当槽函数执行完毕,线程的事件循环可以继续处理其他事件。
    需要小心管理Worker和Thread的生命周期,通常的实践是:
    当工作完成时,Worker发射一个finished信号,连接到线程的quit()槽,使线程退出事件循环。
    将Worker的finished信号连接到Worker的deleteLater槽,以便在线程结束后删除Worker。
    将线程的finished信号连接到线程的deleteLater槽,以便在线程结束后删除线程对象。
  2. 灵活性
    继承QThread方式
    每个线程实例只能执行一个任务(run方法中的任务),如果要执行不同的任务,需要定义不同的QThread子类。
    如果要在同一个线程中执行多个任务,需要在run方法中组织多个任务,这可能会导致代码复杂。
    Worker + moveToThread()方式
    Worker对象可以定义多个槽函数,每个槽函数都可以是一个任务,并且这些槽函数都可以在同一个线程中执行(通过信号触发)。
    同一个Worker对象可以移动到不同的线程,也可以有多个Worker对象在同一个线程中(但需要小心,因为一个对象只能在一个线程中)。
  3. 事件循环
    继承QThread方式
    默认情况下,QThread的run()方法启动一个事件循环(通过调用exec()),但如果我们重写了run()方法,并且没有调用exec(),那么该线程就没有事件循环。
    没有事件循环,就不能在该线程中使用需要事件循环的机制(例如,定时器、网络通信等)。
    Worker + moveToThread()方式
    由于QThread的事件循环是默认启动的(run()方法中调用exec()),所以Worker对象可以使用需要事件循环的功能,例如,在Worker的槽函数中启动一个定时器,或者进行网络通信。
  4. 使用场景
    继承QThread方式
    适用于简单的后台任务,不需要事件循环,或者任务本身就是长时间运行的循环。
    适合一次性任务,任务完成后线程就结束。
    Worker + moveToThread()方式
    适用于需要在后台执行多个任务,或者任务中需要使用事件循环(例如,定时器、网络通信)。
    适合需要重复执行的任务,或者需要在线程中处理多个信号的任务。
相关推荐
凉、介7 小时前
C 语言类型强转引发的隐蔽内存破坏问题分析
c语言·开发语言·笔记·学习·嵌入式
子木HAPPY阳VIP7 小时前
Tomcat 9 + JSP 中文乱码终极解决方案(完整版可复制)
java·开发语言·docker·tomcat·jsp
SteveDraw7 小时前
常见的设计模式及工业场景下应用(更新中)
设计模式·c#·编码规范·gof23
郝学胜-神的一滴7 小时前
epoll 反应堆模型深度拆解:从红黑树到回调闭环,手写高性能回射服务器
linux·运维·服务器·开发语言·c++·unix
csbysj20207 小时前
Bootstrap4 模态框
开发语言
AI玫瑰助手7 小时前
Python基础:输入input与输出print函数详解
开发语言·windows·python
rit84324997 小时前
电容层析成像(ECT)的ART算法MATLAB演示实例
开发语言·算法·matlab
故事和你917 小时前
洛谷-算法2-4-字符串2
开发语言·数据结构·c++·算法·深度优先·动态规划·图论
郝学胜-神的一滴7 小时前
干货版《算法导论》 02 :算法效率核心解密
java·开发语言·数据结构·c++·python·算法