PySide6/PyQT多线程之 线程池的基础概念和最佳实践

举报
frica01 发表于 2023/10/31 21:08:16 2023/10/31
【摘要】 文章介绍了使用线程池的优势,包括提高应用程序性能、减少资源消耗、管理并发执行任务等方面的好处。对使用线程池进行多线程编程提供了清晰的解释和示例代码,可以帮助读者理解并掌握相关的知识和技巧。

前言

PySide6/PyQT 多线程编程中,线程池也是重要的一项知识点,线程池是一种高效管理和调度多个线程执行任务的方式。
通过结合线程池(QThreadPool)和任务类(QRunnable),可以轻松地实现并发执行任务、提高应用程序的性能,并更好地控制代码逻辑和资源的管理。

值得注意的是,在使用 QRunnableQThreadPool 的线程池中,每个任务(QRunnable 对象)会被分配到不同的线程执行,每个线程拥有自己的任务和局部数据。因此,默认情况下,每个任务在自己的线程中是相互独立的,不会直接共享资源。

知识点📖📖

本文用到的几个PySide6/PyQT的知识点及链接。

作用 链接
对象间通信的机制,允许对象发送和接收信号 Signal
用于响应Signal信号的方法 Slot
可运行的任务类,用于封装要在后台线程中执行的代码逻辑 QRunnable
线程池类,用于管理和调度多个线程执行 QRunnable 任务 QThreadPool

基础概念

QRunnable

一个接口类,用于表示可以在线程中执行的任务。通过继承 QRunnable 并实现 run 方法,可以定义任务的逻辑。

QThreadPool

一个管理线程的线程池类。它可以管理和执行 QRunnable 对象,并自动管理线程的创建、回收和重用。

QThreadPool 可以限制并发线程的数量,防止资源过度占用。

QRunnable 更适合用于执行短期、轻量级的任务,而 QThread 更适合用于需要更多控制和状态管理的长期任务。

使用 QRunnableQThreadPool 的优势在于线程的创建和管理交由线程池处理,可以避免频繁创建和销毁线程,提高效率,并根据系统资源和任务数量自动调整线程的数量。



创建线程池步骤

创建自定义任务类(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() 清除线程池中所有待执行的任务,但不会停止正在执行的任务
setMaxThreadCount(100) 设置线程池的最大线程数,线程池默认的最大线程数为当前系统的处理器核心数量。

代码展示

这只是一份简单的线程池模板,没啥用~

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()

代码释义

  1. 定义工作任务类:定义了一个名为Worker的自定义类,它继承自QRunnable类。这个类表示一个工作任务,具体的工作逻辑定义在run方法中。

  2. Worker类的__init__方法:这个方法用于初始化工作任务对象。在这个示例中,将每个工作任务命名为name,并将其保存在self.name属性中。

  3. Worker类的run方法:这个方法表示工作任务的执行逻辑。在示例中,打印工作任务的名称,然后使用time.sleep方法模拟一些耗时的工作(2秒),最后再次打印工作任务的名称。

  4. main函数:程序的入口点。在这个函数中执行以下操作:

    • 创建线程池:使用QThreadPool()创建一个全局的线程池对象thread_pool
    • 设置最大线程数:使用setMaxThreadCount方法设置线程池的最大线程数为4。
    • 创建工作任务并添加到线程池:使用一个循环创建了5个工作任务,每个任务都有一个唯一的名称。然后,使用start方法将这些工作任务添加到线程池中,以便并行执行。
    • 阻塞主线程直到任务完成:使用waitForDone方法阻塞主线程,直到所有任务完成执行。
    • 打印所有任务完成的消息:在所有任务完成后,打印"All tasks completed"的消息。

这份代码创建了一个线程池并将多个工作任务添加到线程池中执行。
每个任务都是通过继承QRunnable类并实现run方法来定义的。通过使用线程池,这些工作任务可以在多个线程中并行执行,从而提高程序的效率。
最后,等待所有任务完成后,打印出任务完成的消息。

总结🎈🎈

本文介绍了PySide6/PyQT线程池的基本概念和使用方法。介绍了QRunnableQThreadPool的作用,以及它们结合在一起的优势。

通过使用QRunnableQThreadPool,可以将任务的执行和线程管理分离,提高应用程序的性能和响应性。

我建议常见的多线程场景都用上线程池,省事~

后话

本次分享到此结束,
see you~~🐱‍🏍🐱‍🏍

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。