
前言
在PySide6/PyQT 多线程编程中,线程池也是重要的一项知识点,线程池是一种高效管理和调度多个线程执行任务的方式。
通过结合线程池(QThreadPool)和任务类(QRunnable),可以轻松地实现并发执行任务、提高应用程序的性能,并更好地控制代码逻辑和资源的管理。
值得注意的是,在使用 QRunnable 和 QThreadPool 的线程池中,每个任务(QRunnable 对象)会被分配到不同的线程执行,每个线程拥有自己的任务和局部数据。因此,默认情况下,每个任务在自己的线程中是相互独立的,不会直接共享资源。
知识点📖📖
本文用到的几个PySide6/PyQT的知识点及链接。
| 作用 | 链接 | 
|---|---|
| 对象间通信的机制,允许对象发送和接收信号 | Signal | 
| 用于响应Signal信号的方法 | Slot | 
| 可运行的任务类,用于封装要在后台线程中执行的代码逻辑 | QRunnable | 
| 线程池类,用于管理和调度多个线程执行 QRunnable 任务 | QThreadPool | 
基础概念
QRunnable
一个接口类,用于表示可以在线程中执行的任务。通过继承 QRunnable 并实现 run 方法,可以定义任务的逻辑。
 
QThreadPool
一个管理线程的线程池类。它可以管理和执行 QRunnable 对象,并自动管理线程的创建、回收和重用。
 
QThreadPool 可以限制并发线程的数量,防止资源过度占用。
QRunnable 更适合用于执行短期、轻量级的任务,而 QThread 更适合用于需要更多控制和状态管理的长期任务。
使用 QRunnable 和 QThreadPool 的优势在于线程的创建和管理交由线程池处理,可以避免频繁创建和销毁线程,提高效率,并根据系统资源和任务数量自动调整线程的数量。
创建线程池步骤
创建自定义任务类(QRunnable)
- 创建继承自 QRunnable 的自定义任务类。在任务类中实现 run() 方法,该方法包含要在后台线程中执行的代码逻辑。
class Worker(QRunnable):
    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self.func = func
        self.args = args
        self.kwargs = kwargs
    def run(self):
        # 执行任务函数,并传递参数
        self.func(*self.args, **self.kwargs)
创建线程池对象(QThreadPool)
- 在应用程序中创建一个线程池对象。通过直接实例化 QThreadPool 或使用 QThreadPool.globalInstance() 方法获取全局线程池实例。
def main():
    # 创建线程池对象
    threadpool = QThreadPool()
创建任务对象并提交给线程池
- 创建一个任务对象,将其实例化为自定义任务类的实例。
- (可选)可以通过构造函数传递任务所需的函数、参数或其他数据。
- 使用线程池的 start() 方法将任务对象提交给线程池执行。
# 示例任务函数
def task_func():
    # 耗时操作
   
# 创建任务对象并提交给线程池
task1 = Worker(task_func, "John")
threadpool.start(task1)
task2 = Worker(task_func, "Alice")
threadpool.start(task2)
与QThread相比较
优势有挺多的,参考如下:
- 将任务的执行和线程管理分离,使代码更具可维护性和可扩展;
- 管理和控制多个任务的并发执行,以最大程度地利用系统资源;
- 在需要执行大量独立任务或耗时操作的情况下,提供一种高效的并发处理方式;
- 提高应用程序的响应性,通过并行执行任务,减少主线程的负载,避免阻塞用户界面;
- 通过将任务逻辑封装在 QRunnable 中,并使用 QThreadPool 进行任务的调度和管理,轻松实现多线程应用程序,提高性能和响应性,并且更好地控制和组织代码。
- 自动管理线程的创建、回收和重用,避免频繁创建和销毁线程,从而减少了线程切换和资源消耗。线程池可以重用线程,避免了重复创建线程的开销。
总结
综上所述,使用 QRunnable 和 QThreadPool 相对于直接使用 QThread 提供了更高级的线程管理功能、更好的性能优化、更好的并发控制和更好的可扩展性。
它通过自动管理线程的创建和回收、动态调整线程数量、限制并发线程的数量等机制,减少了线程切换和资源消耗,提高了整体系统性能和稳定性。
同时,将任务逻辑封装在 QRunnable 中,使代码更清晰和可维护,并提供了更灵活的任务管理方式。
常用方法
threadpool = QThreadPool()
常用的方法列举在这里,
| 代码 | 释义 | 
|---|---|
| start(QRunnable) | 将一个任务(QRunnable 对象)提交给线程池执行 | 
| activeThreadCount() | 返回当前正在执行任务的线程数 | 
| maxThreadCount() | 返回线程池允许的最大线程数 | 
| waitForDone(msecs = -1) | 等待线程池中的所有任务执行完成,或者等待指定的毫秒数。如果设置为 -1,则会一直等待直到所有任务完成 | 
| clear() | 清除线程池中所有待执行的任务,但不会停止正在执行的任务 | 
代码展示
这只是一份简单的线程池模板,没啥用~
import time
from PySide6.QtCore import (QRunnable, QThreadPool, Qt)
# 自定义的工作任务类
class Worker(QRunnable):
    def __init__(self, name):
        super().__init__()
        self.name = name
    def run(self):
        print(f"Worker {self.name} started")
        time.sleep(2)  # 模拟一些耗时的工作
        print(f"Worker {self.name} finished")
def main():
    # 创建线程池
    thread_pool = QThreadPool()
    # 设置最大线程数
    thread_pool.setMaxThreadCount(4)
    # 创建工作任务并将其添加到线程池
    for i in range(1, 6):
        worker = Worker(f"Task {i}")
        thread_pool.start(worker)
    # 在等待所有任务完成之前,阻塞主线程
    thread_pool.waitForDone()
    print("All tasks completed")
if __name__ == "__main__":
    main()
代码释义
-  定义工作任务类:定义了一个名为 Worker的自定义类,它继承自QRunnable类。这个类表示一个工作任务,具体的工作逻辑定义在run方法中。
-  Worker类的__init__方法:这个方法用于初始化工作任务对象。在这个示例中,将每个工作任务命名为name,并将其保存在self.name属性中。
-  Worker类的run方法:这个方法表示工作任务的执行逻辑。在示例中,打印工作任务的名称,然后使用time.sleep方法模拟一些耗时的工作(2秒),最后再次打印工作任务的名称。
-  main函数:程序的入口点。在这个函数中执行以下操作:- 创建线程池:使用QThreadPool()创建一个全局的线程池对象thread_pool。
- 设置最大线程数:使用setMaxThreadCount方法设置线程池的最大线程数为4。
- 创建工作任务并添加到线程池:使用一个循环创建了5个工作任务,每个任务都有一个唯一的名称。然后,使用start方法将这些工作任务添加到线程池中,以便并行执行。
- 阻塞主线程直到任务完成:使用waitForDone方法阻塞主线程,直到所有任务完成执行。
- 打印所有任务完成的消息:在所有任务完成后,打印"All tasks completed"的消息。
 
- 创建线程池:使用
这份代码创建了一个线程池并将多个工作任务添加到线程池中执行。
 每个任务都是通过继承QRunnable类并实现run方法来定义的。通过使用线程池,这些工作任务可以在多个线程中并行执行,从而提高程序的效率。
 最后,等待所有任务完成后,打印出任务完成的消息。
总结🎈🎈
本文介绍了PySide6/PyQT线程池的基本概念和使用方法。介绍了QRunnable和QThreadPool的作用,以及它们结合在一起的优势。
通过使用QRunnable和QThreadPool,可以将任务的执行和线程管理分离,提高应用程序的性能和响应性。
我建议常见的多线程场景都用上线程池,省事~
后话
本次分享到此结束,
 see you~~🐱🏍🐱🏍



















