PySide6/PyQT多线程之 多线程 与 线程池的模板(拿来即用)

举报
frica01 发表于 2023/10/31 21:10:41 2023/10/31
【摘要】 本文给出了的多线程以及线程池的基础使用模板,方便后面有需要时候直接拿来就用。

前言

关于PySide6/PyQT多线程系列的最后一篇。写这篇文章的动机是方便后续代码的直接复用。

本篇文章实际是水文,给出了 PySide6/PyQT的多线程以及线程池的基础使用模板,方便后面有需要时候直接拿来就用。


多线程

这里分两种情况来谈论,有返回值 与 无返回值。

无返回值

代码释义

定义 MyThread 的线程类,继承自 QThread
在构造函数中,接收一个函数 func 和其它参数 *args 和 **kwargs
并在 run() 方法中,调用传入的函数,并将参数传递给它。

定义 do_something() 的函数,它表示一个耗时的操作。

在主程序中,实例化MyThread 对象 thread,将 do_something 函数作为参数传递给它。
然后,通过调用 start() 方法启动线程,线程会自动执行 run() 方法中的任务。


# -*- coding: utf-8 -*-

import time
from PySide6.QtCore import (Qt, QThread)


# 自定义的工作任务类
class MyThread(QThread):
    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)


def do_something():
    time.sleep(100)
    return '耗时操作'


if __name__ == '__main__':
    thread = MyThread(do_something)
    thread.start()

有返回值

有返回值,也分两种情况来讨论

  • 不推荐)使用线程对象的 wait() 方法等待子线程执行完毕,然后通过自定义的方法或属性来获取返回值
  • 推荐) 使用信号和槽机制进行线程间通信

不推荐

这种情况是不推荐的,不够线程安全,
在多线程编程中,共享数据可能存在竞争条件和线程安全性问题,从而引发线程安全性问题。

# -*- coding: utf-8 -*-

import time
from PySide6.QtCore import (Qt, QThread)


# 自定义的工作任务类
class MyThread(QThread):
    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self.res = str()
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def run(self):
        self.res = self.func(*self.args, **self.kwargs)


def do_something():
    time.sleep(100)
    return '耗时操作'


if __name__ == '__main__':
    thread = MyThread(do_something)
    thread.start()
    thread.wait()
    print(thread.res)

推荐


代码释义

整体流程如下:

  • 创建继承QThreadMyThead类,接收一个函数和不定长传参;重写了 run方法并在这里执行传递过来的函数和传参;
  • 创建继承自 QWidgetMainWindow 类,用于显示窗口和处理事件;
  • setup_ui 方法中设置窗口的标题、大小,并创建标签和按钮,并将它们添加到垂直布局中;
  • setup_thread 方法中创建 MyThread 线程对象,并连接它的 finish 信号到槽函数 thread_finished
  • do_something函数中模拟一个耗时的任务,并返回任务的结果;
  • thread_finished 槽函数中,接收到任务完成的信号后,将结果更新到标签上;
  • 运行程序后,窗口会显示一个标签和一个按钮。点击按钮后,耗时任务会在后台执行,执行完成后,结果会显示在标签上。


# -*- coding: utf-8 -*-

import time

from PySide6.QtCore import (QThread, Signal, Slot, QSize)
from PySide6.QtWidgets import (QApplication, QPushButton, QLabel, QVBoxLayout, QWidget)


class MyThread(QThread):
    finish = Signal(str)

    def __init__(self, func, *args, **kwargs):
        super().__init__()
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def run(self):
        res = self.func(*self.args, **self.kwargs)
        # 任务完成后发出信号
        self.finish.emit(res)


def do_something():
    time.sleep(1)
    return 'the function execution completed!'


class MainWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.setup_ui()
        self.button.clicked.connect(self.setup_thread)

    def setup_ui(self):
        self.setWindowTitle('demo')
        self.resize(QSize(250, 180))
        # 创建一个垂直布局
        layout = QVBoxLayout()
        # 创建一个标签
        self.label = QLabel('This is a label => ')
        layout.addWidget(self.label)
        # 创建一个按钮
        self.button = QPushButton('execute')
        layout.addWidget(self.button)
        # 将布局设置为主窗口的布局
        self.setLayout(layout)
        # 显示窗口
        self.show()

    def setup_thread(self):
        self.thread = MyThread(do_something)
        self.thread.finish.connect(self.thread_finished)
        self.thread.start()

    @Slot(str)
    def thread_finished(self, res):
        self.label.setText('This is a label => ' + res)


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

线程池

代码释义

MyWorker类继承自QRunnable,接收一个函数、一个信号和其他可变参数。在run方法中,它调用了传入的函数并将结果发射出信号。

do_something函数是一个模拟的耗时操作,它在执行完毕后返回一个字符串。

MainWindow类是窗口的主要部分,它创建了一个垂直布局,并添加了一个标签和一个按钮。点击按钮后,它创建一个MyWorker对象,并使用线程池启动这个线程。同时,它将信号与thread_finished槽函数连接起来,在线程完成后更新标签的文本。

# -*- coding: utf-8 -*-

import time

from PySide6.QtCore import (QRunnable, QThreadPool, Signal, Slot, QSize)
from PySide6.QtWidgets import (QApplication, QPushButton, QLabel, QVBoxLayout, QWidget)


class MyWorker(QRunnable):

    def __init__(self, func, signal, *args, **kwargs):
        super().__init__()
        self.func = func
        self.args = args
        self.kwargs = kwargs
        self.signal = signal

    def run(self):
        res = self.func(*self.args, **self.kwargs)
        # 任务完成后发出信号
        self.signal.emit(res)


def do_something():
    time.sleep(1)
    return 'the function execution completed!'


class MainWindow(QWidget):
    signal = Signal(str)

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.thread_pool = QThreadPool()
        self.setup_ui()
        self.button.clicked.connect(self.setup_thread)

    def setup_ui(self):
        self.setWindowTitle('demo')
        self.resize(QSize(250, 180))
        # 创建一个垂直布局
        layout = QVBoxLayout()
        # 创建一个标签
        self.label = QLabel('This is a label => ')
        layout.addWidget(self.label)
        # 创建一个按钮
        self.button = QPushButton('execute')
        layout.addWidget(self.button)
        # 将布局设置为主窗口的布局
        self.setLayout(layout)
        # 显示窗口
        self.show()

    def setup_thread(self):
        worker = MyWorker(do_something, self.signal)
        self.thread_pool.start(worker)
        self.signal.connect(self.thread_finished)

    @Slot(str)
    def thread_finished(self, res):
        self.label.setText('This is a label => ' + res)


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

复用建议

在后续复用中,有以下注意事项

多线程线程池都需要注意的是:

  • run方法中考虑异常捕获;
  • 线程安全性: 在多线程环境下,要注意共享数据的线程安全性。

多线程

  • 线程的生命周期管理: 从创建到销毁的管理。确保在线程执行完毕后,适时释放线程资源,避免线程泄漏或过多的线程资源占用。

线程池

  • 线程池的最大线程数:可以通过修改setMaxThreadCount(n) 来设置线程池的最大线程数。
  • 线程销毁:可以考虑在线程执行完毕后进行销毁操作,以释放资源。可以通过在 MyWorker 的 run 方法中添加 self.setAutoDelete(True) 来实现。

但最重要的是根据实际情况来修改!

后话

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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