PySide6/PyQT多线程之 高效管理多线程:暂停、恢复和停止的最佳实践

举报
frica01 发表于 2023/10/31 21:05:14 2023/10/31
【摘要】 本文中,展示了使用实现多线程编程,实现了一个具有启动、暂停、恢复和终止功能的线程。本文虽然是一个简单的示例,但它也将多线程开发中该用到的知识点都用上了,算是抛砖引玉吧。希望本文能够帮助读者理解和应用 PySide6 的多线程功能。

前言

关于 PySide6/PyQT 多线程,正确地处理多线程编程并确保线程之间的同步和通信并不容易。
本文以一个示例代码为基础,介绍 PySide6/PyQT多线程的运用,展示如何创建和管理线程,以及如何实现线程之间的同步和通信。

设想这么一个场景:

  • 在实际开发过程中,在涉及到长时间运行的计算任务时,用户可能希望能够暂停、恢复和结束线程的执行,以便更好地控制程序的行为;
  • 如线程间的同步和通信、线程的暂停、恢复和结束等;
  • ...

本专栏前面几篇文章,几乎覆盖了PySide6/PyQT 多线程编程开发中的100%,
PySide6/PyQT中, QProgressBar 控件完美适配了本文的主旨。

于是乎,这篇文章就将前面几篇 PySide6/PyQT 的文章串起来,使用QProgressBar 组件写一个进度条相关的GUI工具,方便读者更加深入的去理解多线程的使用。


值得一提的是,本文的代码是基于下面这篇文章的示例代码。


知识点📖📖

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

作用 链接
创建新线程 QThread
对象间通信的机制,允许对象发送和接收信号 Signal
用于响应Signal信号的方法 Slot
线程同步机制,用于协调多个线程之间对共享资源的访问 QMutex
锁定互斥锁的对象,简化代码,避免手动处理锁的加锁和解锁操作 QMutexLocker
线程同步机制,一般配合 QMutex 使用 QWaitCondition
进度条控件 QProgressBar

实现

这里是对完整代码拆解再讲解,

创建线程

  • 重写run()方法,该方法会在新线程启动时候执行;
  • 新增两个方法,分别是暂停和恢复线程
class MyThread(QThread):

    def __init__(self, parent=None):
        super().__init__(parent=parent)

    def pause_thread(self):
        # 在新线程中执行的暂停的代码

    def resume_thread(self):
        # 在新线程中执行的恢复的代码

    def run(self):
		# 在新线程中执行的代码

启动线程

  • 实例化类,再调用 start_thread() 启动线程
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setup_ui()
        self.setup_thread()

    def setup_ui(self):
		# GUI界面绘制

    def setup_thread(self):
        self.thread = MyThread()

    def start_thread(self):
        self.thread.start()

    def paused_thread(self):
        self.thread.pause_thread()

    def resume_thread(self):
        self.thread.resume_thread()
        
    def stop_thread(self):
        self.thread.quit()
my_thread = MyThread()
my_thread
my_thread.start()

暂停线程

  • 调用 pause_thread() 方法,就会暂停线程
self.thread.pause_thread()

恢复线程

  • 调用 resume_thread() 方法,就会恢复线程运行
self.thread.resume_thread()

终止线程

  • 调用 quit() 方法,就会停止线程
self.thread.quit()

完整代码

该代码实现了一个具有启动、暂停、恢复和终止功能的线程,并将进度值显示在进度条上。

# -*- coding: utf-8 -*-
# Name:         demo.py
# Author:       小菜
# Date:         2023/5/4
# Description:

import sys
from PySide6.QtCore import (QThread, QWaitCondition, QMutex, Signal, QMutexLocker)
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QPushButton, QProgressBar, QApplication)


class MyThread(QThread):
    valueChange = Signal(int)

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.is_paused = bool(0)  # 标记线程是否暂停
        self.progress_value = int(0)  # 进度值
        self.mutex = QMutex()  # 互斥锁,用于线程同步
        self.cond = QWaitCondition()  # 等待条件,用于线程暂停和恢复

    def pause_thread(self):
        with QMutexLocker(self.mutex):
            self.is_paused = True  # 设置线程为暂停状态

    def resume_thread(self):
        if self.is_paused:
            with QMutexLocker(self.mutex):
                self.is_paused = False  # 设置线程为非暂停状态
                self.cond.wakeOne()  # 唤醒一个等待的线程

    def run(self):
        while True:
            with QMutexLocker(self.mutex):
                while self.is_paused:
                    self.cond.wait(self.mutex)  # 当线程暂停时,等待条件满足
                if self.progress_value >= 100:
                    self.progress_value = 0
                    return  # 当进度值达到 100 时,重置为 0 并退出线程
                self.progress_value += 1
                self.valueChange.emit(self.progress_value)  # 发送进度值变化信号
                self.msleep(30)


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.thread_running = False  # 标记线程是否正在运行
        self.setup_ui()
        self.setup_thread()

    def setup_ui(self):
        layout = QVBoxLayout(self)
        self.progressBar = QProgressBar(self)
        layout.addWidget(self.progressBar)
        layout.addWidget(QPushButton(r'启动', self, clicked=self.start_thread))
        layout.addWidget(QPushButton(r'停止', self, clicked=self.paused_thread))
        layout.addWidget(QPushButton(r'恢复', self, clicked=self.resume_thread))
        layout.addWidget(QPushButton(r'结束', self, clicked=self.stop_thread))
        self.show()

    def setup_thread(self):
        self.thread = MyThread()
        self.thread.valueChange.connect(self.progressBar.setValue)
        self.thread_running = True

    def start_thread(self):
        if self.thread_running:
            self.thread.start()
        if not self.thread_running:
            self.setup_thread()
            self.thread.start()

    def paused_thread(self):
        if not self.thread_running:
            return
        if not self.thread.isRunning():
            self.thread.start()
        else:
            self.thread.pause_thread()

    def resume_thread(self):
        if not self.thread_running:
            return
        self.thread.resume_thread()

    def stop_thread(self):
        self.thread.quit()  # 终止线程的事件循环
        self.thread_running = False  # 标记线程停止
        self.progressBar.setValue(0)  # 重置进度条的值


if __name__ == '__main__':
    app = QApplication()
    window = MainWindow()
    sys.exit(app.exec())

代码释义

MyThread 类

MyThread类继承自QThread,是一个自定义的线程类。它通过发射信号来通知界面更新进度条的值。

属性

  • valueChange:自定义的信号,用于发送进度值变化的信号。
  • is_paused:一个布尔值,用于标记线程是否暂停。
  • progress_value:一个整数,表示进度值。
  • mutex:QMutex对象,用于线程同步。
  • cond:QWaitCondition对象,用于线程暂停和恢复。

方法

  • __init__(self, parent=None):构造函数,用于初始化对象。
  • pause_thread(self):暂停线程的方法。
  • resume_thread(self):恢复线程的方法。
  • run(self):线程执行的方法。在一个无限循环中,判断线程是否暂停,如果是则等待件满足;否则,增加进度值,并发射进度值变化的信号。
  • msleep(self, milliseconds):线程休眠的方法,以毫秒为单位。

MainWindow类

MainWindow 类是一个继承自 QWidget 的窗口类。它包含了一些状态变量和方法来管理线程和界面的交互。

属性

  • thread_running:一个布尔值,标记线程是否正在运行

方法

  • setup_ui:设置用户界面。创建一个垂直布局,并在布局中添加了一个进度条 QProgressBar 和四个按钮 QPushButton。这些按钮分别是 “启动”、“停止”、“恢复” 和 “结束”。
  • setup_thread:设置线程。实例化一个 MyThread 对象,并将进度值变化的信号 valueChange 与进度条的 setValue 方法连接起来。在线程运行时,进度条的值会随着信号的发出而更新。同时将 thread_running 标志设置为 True,表示线程正在运行。
  • start_thread:启动线程。如果 thread_running 为 True,表示线程已经存在,直接调用 start 方法来启动线程。如果 thread_running 为 False,则调用 setup_thread 方法来创建并设置线程,然后再启动线程。
  • paused_thread:用于暂停线程。如果线程没有运行,即 isRunning() 返回 False,则调用 start 方法来启动线程。否则,调用线程的 pause_thread 方法来暂停线程的执行。
  • resume_thread:用于恢复线程的执行。如果线程没有运行,即 thread_running 为 False,则直接返回。否则,调用线程的 resume_thread 方法来恢复线程的执行。
  • stop_thread:用于停止线程。它调用线程的 quit 方法来终止线程的事件循环,并将 thread_running 标志设置为 False,表示线程已停止。同时,将进度条的值重置为 0

运行结果

总结🎈🎈

本文中,展示了使用 PySide6/PyQT实现多线程编程,实现了一个具有启动、暂停、恢复和终止功能的线程。

后话

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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