甘孜藏族自治州网站建设_网站建设公司_RESTful_seo优化
2026/1/9 18:58:09 网站建设 项目流程

简要介绍:

  • QRunnable是轻量级的任务载体
  1. 核心定位:只封装「任务逻辑」,不具备线程能力。QRunnable的唯一核心作用是「打包要在子线程中执行的业务代码」,它只回答「要做什么」(即run()方法中封装的逻辑),本身不是线程,无法独立运行,必须依赖QThreadPool才能被调度执行。

  2. 「轻量级」的核心体现

    • 结构极简:仅需重写一个run()方法即可使用,无其他强制重写的抽象方法;
    • 无额外开销:不继承QObject,没有 Qt 信号槽、事件循环等附加机制,创建和销毁的资源消耗极低;
    • 无需手动管理生命周期:默认autoDelete=True,任务执行完毕后,QThreadPool会自动销毁其实例,无需手动绑定finished信号和deleteLater(),避免内存泄露且减少代码工作量。
    • 对比凸显轻量
      QThread相比,QThread既是「线程载体」又是「生命周期管理者」,自带线程启停、事件循环等复杂逻辑;而QRunnable只专注于「任务本身」,把线程的创建、调度、复用全交给QThreadPool,更适合执行简单、短期的临时任务。

  • QThreadPool.globalInstance()是获取全局线程池实例的语句:
  1. 所属框架与核心类:该语句依赖 Qt 框架的QThreadPool类,该类是 Qt 提供的线程池管理工具,用于统一管理、复用线程,避免频繁创建 / 销毁线程带来的性能开销,是 Qt 中实现多线程任务(尤其是短小、大量的任务)的常用方案。

  2. globalInstance()方法的核心作用:globalInstance()QThreadPool静态方法(static method),它的核心功能不是创建新的线程池对象,而是获取 Qt 框架内置的、整个应用程序级别的全局唯一线程池实例

    • 这个实例是 Qt 在应用程序启动时自动初始化的,全程单例(唯一);
    • 无需开发者手动创建QThreadPool对象,直接通过该方法获取实例后即可随时复用全局线程池,简化多线程代码实现。
  3. 语句的执行结果与后续用途:语句执行后,随时可以引用(指向)这个全局唯一的 QThreadPool 实例(而非创建新实例),后续开发者可以通过实例调用QThreadPool的成员方法(如start()提交任务、setMaxThreadCount()设置最大线程数等),实现多线程任务的提交与管理。

  4. 补充核心特性:全局线程池的生命周期与整个 Qt 应用程序绑定,应用程序退出时自动销毁,无需开发者手动释放资源,降低了多线程开发的资源管理成本。

使用方法:

二者结合,非常适合执行简单、短期的任务,Qt 在应用程序启动初始化一个线程池实例:QThreadPool.globalInstance(),然后创建QRunnable实例,再使用QThreadPool.globalInstance()实例的start()方法启动QRunnable实例,与QThread 线程相比,代码简洁且无需关心线程销毁(线程池自动管理)。

  • 最简化代码:
import sys import time from PySide6.QtCore import QMutex, QRunnable, QThreadPool, QMutexLocker from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout # 1. 包装QRunnable任务(极简包装,无需复杂继承逻辑) class Runnable(QRunnable): def __init__(self, buf_lock): super().__init__() self.buf_lock = buf_lock # QRunnable接口的实现:run()方法 def run(self): with QMutexLocker(self.buf_lock): # 临界区:获取互斥锁后执行业务函数 print("临时线程:获取锁,执行操作") # 模拟耗时操作 time.sleep(5) # 耗时操作在互斥锁的临界区内执行 print("临时任务执行完毕") # 2. 按钮点击触发函数(创建任务 + 提交到线程池,无需手动启动线程) def on_button_clicked(lock): """点击按钮触发,快速提交临时任务到线程池""" runnable = Runnable(lock) thread_pool.start(runnable) # 线程池自动分配线程执行任务 if __name__ == '__main__': app = QApplication(sys.argv) lock = QMutex() # 全局互斥锁 thread_pool = QThreadPool.globalInstance() # 获取全局线程池(无需手动创建) # 初始化按钮,绑定点击事件 btn = QPushButton("创建临时线程执行 1 操作") btn.clicked.connect(lambda: on_button_clicked(lock)) btn.show() sys.exit(app.exec())
  • 将任务封装成函数:
import sys import time from PySide6.QtCore import QMutex, QRunnable, QThreadPool, QMutexLocker from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout # 1. 定义简单的任务执行函数(无需创建QObject/QThread子类,仅处理核心逻辑) def task1(): print("临时线程1:获取锁,执行 1 操作") def task2(): print("临时线程2:获取锁,执行 2 操作") # 2. 包装QRunnable任务(极简包装,无需复杂继承逻辑) class Runnable(QRunnable): def __init__(self, buf_lock, task): super().__init__() self.buf_lock = buf_lock self.task = task # QRunnable接口的实现:run()方法 def run(self): with QMutexLocker(self.buf_lock): # 临界区:获取互斥锁后执行业务函数 self.task() # 模拟耗时操作 # time.sleep(5) # 耗时操作在互斥锁的临界区内执行 or: time.sleep(5) # 耗时操作在互斥锁的临界区外执行,可以体会一下二者的区别 print(f"临时任务:{self.task.__name__}执行完毕") # 3. 按钮点击触发函数(创建任务 + 提交到线程池,无需手动启动线程) def on_button_clicked(lock, task): """点击按钮触发,快速提交临时任务到线程池""" runnable = Runnable(lock, task) thread_pool.start(runnable) # 线程池自动分配线程执行任务 if __name__ == '__main__': app = QApplication(sys.argv) lock = QMutex() # 全局互斥锁 thread_pool = QThreadPool.globalInstance() # 获取全局线程池(无需手动创建) # 初始化按钮,绑定点击事件 btn1 = QPushButton("创建临时线程执行 1 操作") btn1.clicked.connect(lambda: on_button_clicked(lock, task1)) btn2 = QPushButton("创建临时线程执行 2 操作") btn2.clicked.connect(lambda: on_button_clicked(lock, task2)) form = QWidget() layout = QVBoxLayout(form) layout.addWidget(btn1) layout.addWidget(btn2) form.setLayout(layout) form.show() sys.exit(app.exec())
  • 将工作的对象封装为类:
import sys import time from PySide6.QtCore import QMutex, QRunnable, QThreadPool, QMutexLocker, QObject from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout # 工作的对象 class WorkObject(QObject): def __init__(self, lock): super().__init__() self.lock = lock # 按钮点击触发函数(创建任务 + 提交到线程池,无需手动启动线程) def on_button_clicked(self): """点击按钮触发,快速提交临时任务到线程池""" runnable = Runnable(self.lock) thread_pool.start(runnable) # 线程池自动分配线程执行任务 # 包装QRunnable任务 class Runnable(QRunnable): def __init__(self, buf_lock): super().__init__() self.buf_lock = buf_lock # QRunnable接口的实现:run()方法 def run(self): with QMutexLocker(self.buf_lock): # 临界区:获取互斥锁后执行业务函数 print(f"临时线程:获取锁,执行操作") # 模拟耗时操作 # time.sleep(5) # 耗时操作在互斥锁的临界区内执行 or: time.sleep(5) # 耗时操作在互斥锁的临界区外执行 print(f"临时任务执行完毕") if __name__ == '__main__': app = QApplication(sys.argv) lock = QMutex() # 全局互斥锁 thread_pool = QThreadPool.globalInstance() # 获取全局线程池(无需手动创建) obj_thread = WorkObject(lock) # 创建对象线程对象 # 初始化按钮,绑定点击事件 btn1 = QPushButton("创建临时线程执行操作") btn1.clicked.connect(lambda: obj_thread.on_button_clicked()) btn1.show() sys.exit(app.exec())

上面的代码,每次点击按钮,都定义了一个QRunnable,并在线程池中自动运行,运行完成后自动销毁。另外,代码中模拟的耗时操作time.sleep(5)放在互斥锁的临界区内还是外执行,区别还是很明显的。

工程意义:

比如,一个相机采图项目,当从相机中获取到了像素数据后,需要进行保存,就可以把保存的操作封装为QRunnable,并且只在互斥锁中执行内存拷贝,写硬盘的操作不影响相机的采图线程:

import sys import time from copy import deepcopy from PySide6.QtCore import QMutex, QRunnable, QThreadPool, QMutexLocker, QThread, Signal from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout, QLabel def save_bmp(): global frame_data time.sleep(0.1) # 模拟复制像素数据到内存的耗时过程 print(f"获取到了\"{frame_data}\"bmp数据到内存") def save_jpg(): global frame_data time.sleep(0.1) # 模拟复制像素数据到内存的耗时过程 print(f"获取到了\"{frame_data}\"jpg数据到内存") # 包装QRunnable任务 class Runnable(QRunnable): def __init__(self, buf_lock, task): super().__init__() self.buf_lock = buf_lock # 共享的互斥锁对象 self.task = task # 任务函数 # QRunnable接口的实现:run()方法 def run(self): with QMutexLocker(self.buf_lock): # 使用QMutexLocker自动获取/释放互斥锁 # 临界区:获取互斥锁后执行业务函数 self.task() # 执行任务函数 print(f"任务:{self.task.__name__}已释放互斥锁,开始在硬盘写数据") time.sleep(2) # 模拟保存文件的耗时过程 print(f"任务:{self.task.__name__}硬盘写数据完毕") # 3. 按钮点击触发函数(创建任务 + 提交到线程池,无需手动启动线程) def on_button_clicked(lock, task): """点击按钮触发,快速提交临时任务到线程池""" runnable = Runnable(lock, task) # 创建QRunnable任务对象 thread_pool.start(runnable) # 线程池自动分配线程执行任务 # 4. 模拟相机的采样 class CameraQthread(QThread): outFrame = Signal(int) # 模拟相机输出的帧数据 def __init__(self, lock): super().__init__() self.lock = lock # 共享的互斥锁对象 self.frame_num = 0 # 用帧数计数器模拟相机采样得到的帧数据 def run(self): global frame_data while True: with QMutexLocker(self.lock): time.sleep(0.2) # 模拟相机采样耗时 self.frame_num += 1 # 帧数计数,模拟采样到的数据 frame_data = deepcopy(self.frame_num) # 模拟采样到的数据拷贝到全局的帧数据,深度拷贝,防止数据被修改 self.outFrame.emit(self.frame_num) # 发送信号,模拟界面显示相机采样到的图像 if __name__ == '__main__': app = QApplication(sys.argv) lock = QMutex() # 全局互斥锁 thread_pool = QThreadPool.globalInstance() # 获取全局线程池(无需手动创建) frame_data = 0 # 全局帧数据,模拟相机采样得到的数据 camera = CameraQthread(lock) # 创建相机线程对象 camera.start() # 初始化按钮,绑定点击事件 btn1 = QPushButton("保存为bmp格式") btn1.clicked.connect(lambda: on_button_clicked(lock, save_bmp)) btn2 = QPushButton("保存为jpg格式") btn2.clicked.connect(lambda: on_button_clicked(lock, save_jpg)) # 添加一个标签,模拟相机的采样 label = QLabel("相机帧数据") camera.outFrame.connect(lambda num:label.setText(f"相机帧数据:{num}")) # 初始化窗口 form = QWidget() layout = QVBoxLayout(form) layout.addWidget(label) layout.addWidget(btn1) layout.addWidget(btn2) form.setLayout(layout) form.show() sys.exit(app.exec())

从运行截图中可以看到,由于将写硬盘这种耗时工作放在了互斥锁的临界区外,互斥锁只在内存拷贝阶段对相机的采图有影响,而保存到硬盘的操作对相机的采图没有任何影响。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询