【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()方式
    适用于需要在后台执行多个任务,或者任务中需要使用事件循环(例如,定时器、网络通信)。
    适合需要重复执行的任务,或者需要在线程中处理多个信号的任务。
相关推荐
我不是懒洋洋2 小时前
大语言模型(LLM)入门:从Transformer到ChatGPT
c语言·开发语言·c++
MY_TEUCK2 小时前
【Java 后端 | 微服务远程调用实战】Nacos + OpenFeign 从入门到公共模块抽取
java·开发语言·微服务
love_muming2 小时前
Java编程核心技巧全解析
java·开发语言·idea
wjm0410062 小时前
简单谈谈ios开发中的UI
开发语言·ios·swift
slandarer2 小时前
MATLAB | 土地利用变化桑基图及状态转移桑基图绘制
开发语言·数学建模·matlab·桑基图
L_09072 小时前
【C++】面向对象三大特性之多态
开发语言·c++
threelab2 小时前
Three.js 银河星系效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能
程序员敲代码吗2 小时前
探索JavaScript对象创建的灵活方式
开发语言·javascript·ecmascript
FlyWIHTSKY2 小时前
Next.js中客户端组件和服务端组件
开发语言·javascript·ecmascript
天若有情6732 小时前
轻量级状态事件总线 eventbusx-js 开源使用教程
开发语言·javascript·npm·开源·事件·事件总线