31. 多线程技术
一、多线程技术
在 Pyside6 中,主线程一般负责界面绘制和更新。当执行某些复杂且耗时的操作时,如果将执行这些操作的代码放在主线程中,界面就会出现停止响应(或卡顿)的情况。
QThread
类是 PySide6 中的核心线程类,要实现一个线程,需要创建 QThread
类的有一个子类,并且实现其 run()
方法。
线程也有自己的生命周期,其中包含 5 种状态,分别为:新建状态、就绪状态、运行状态、阻塞状态 和 死亡状态。新建状态 就是线程被创建时的状态;当线程对象调用 start()
方法后,线程就处于 就绪状态,当线程得到系统资源后就进入 运行状态。
一旦线程进入运行状态,它会在就绪和运行状态下切换,同时也可能进入阻塞或死亡状态。当处于 运行状态 下的线程在调用 sleep()
、wait()
方法或者发生阻塞时,会进入 暂停状态;当在休眠结束或者阻塞解除时,线程会重新进入 就绪状态;当线程的 run()
方法执行完毕,或者线程发生错误、异常时,线程就进入 死亡状态。
我们可以在终端中使用 pip
安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip
指令后通过 -i
指定国内镜像源下载。
pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple
国内常用的 pip
下载源列表:
- 阿里云 https://mirrors.aliyun.com/pypi/simple
- 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple
- 清华大学 https://pypi.tuna.tsinghua.edu.cn/simple
- 中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple
QThread
类的常用方法及其说明如下:
start(priority:QThread.Priority=QThread.Priority.InheritPriority) -> None # 启动线程
run() -> None # 线程的起点
wait(time:int) -> None # 阻塞线程
usleep(us:int) -> None # 以微秒为单位休眠线程
msleep(ms:int) -> None # 以毫秒为单位休眠线程
sleep(s:int) -> None # 以秒为单位休眠线程
exit(retcode:int=0) -> int # 退出线程的事件循环,并返回代码,返回0表示成功,任何非0值都表示错误
quit() -> int # 退出线程的事件循环,并返回代码0(成功),相当于exit(0)
terminate() -> None # 强制终止线程
setPriority(priority:QThread.Priority.IdlePriority) -> None # 设置线程的优先级
isRunning() -> bool # 获取线程是否在运行
isFinished() -> bool # 获取线程是否完成
QThread
类常用的信号及其说明如下:
started() # 在调用run()方法之前,在相关线程开始执行时从该线程发射
finished() # 在相关线程完成执行之前,从该线程发射
我们可以调用 setPriority(priority:QThread.Priority)
方法来 设置线程的优先级,其中参数 priority
是 QThread.Priority
类型的枚举值,可以取值如下:
QThread.Priority.IdlePriority # 空闲优先级
QThread.Priority.LowestPriority # 最低优先级
QThread.Priority.LowPriority # 低优先级
QThread.Priority.NormalPriority # 系统默认优先级
QThread.Priority.HighPriority # 高优先级
QThread.Priority.HighestPriority # 最高优先级
QThread.Priority.TimeCriticalPriority # 尽可能频繁地分配执行
QThread.Priority.InheritPriority # 默认用与创建线程相同地优先级
在调用 start()
方法 启动线程 后,新创建的线程将调用 run()
方法开始执行。如果我们向强制终止线程,可以调用 terminate()
方法,在 terminate()
方法之后应该使用 wait()
方法,以确保当线程终止时,等待完成的所有线程都将被唤醒。
新建一个 thread.py 文件,用来存放线程相关的代码。
from PySide6.QtCore import QThread, Signal
class MyThread(QThread):
number_signal = Signal(int) # 创建信号
def __init__(self):
super(MyThread, self).__init__() # 调用父类的初始化方法
self.flag = True
self.number = 0
def run(self):
"""重写run()方法"""
self.flag = True
self.number = 0
while True:
if self.flag:
self.number += 1
self.number_signal.emit(self.number) # 发送信号
self.msleep(100) # 线程休眠100毫秒
新建一个 ui.py 文件,用来存放 UI 相关的代码。
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QLabel, QPushButton
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtCore import Qt
class MyUi:
def setupUi(self, window:QWidget):
window.resize(100, 100) # 1.设置窗口对象大小
layout = QVBoxLayout(window) # 2.创建一个垂直布局
self.label = QLabel() # 3.创建一个标签,并添加到垂直布局中
layout.addWidget(self.label)
self.label.setAlignment(Qt.AlignmentFlag.AlignCenter) # 4.设置标签的文本居中
self.label.setText("0") # 5.设置标签的文本
self.start_button = QPushButton("开始") # 6.创建一个按钮,并添加到垂直布局中
layout.addWidget(self.start_button)
self.stop_button = QPushButton("停止")
layout.addWidget(self.stop_button)
新建一个 widget.py 文件,用来存放业务逻辑相关的代码。
import sys
from PySide6.QtWidgets import QApplication, QWidget
from ui import MyUi
from thread import MyThread
class MyWidget(QWidget):
def __init__(self):
super().__init__() # 1.调用父类Qwidget类的__init__()方法
self.__ui = MyUi()
self.__ui.setupUi(self) # 2.初始化页面
self.count_thread = MyThread() # 3.创建线程对象
self.__ui.start_button.clicked.connect(self.start_button_clicked)
self.__ui.stop_button.clicked.connect(self.stop_button_clicked)
self.count_thread.number_signal.connect(self.update_label)
def start_button_clicked(self):
if self.__ui.start_button.text() == "开始":
if not self.count_thread.isRunning(): # 1.如果线程没有运行
self.count_thread.start() # 2.启动线程
else: # 3.如果线程正在运行
self.count_thread.flag = True # 4.设置线程的运行标志为True
self.__ui.start_button.setText("暂停")
elif self.__ui.start_button.text() == "暂停":
self.count_thread.flag = False # 5.设置线程的运行标志为False
self.__ui.start_button.setText("开始")
elif self.__ui.start_button.text() == "重启":
if not self.count_thread.isRunning():
self.count_thread.start()
self.__ui.start_button.setText("暂停")
def stop_button_clicked(self):
self.count_thread.terminate()
self.count_thread.wait()
self.__ui.start_button.setText("重启")
def update_label(self, value:int):
self.__ui.label.setText(str(value))
if __name__ == "__main__":
app = QApplication(sys.argv) # 1.创建一个QApplication类的实例
window = MyWidget() # 2.创建一个窗口
window.show() # 3.展示窗口
sys.exit(app.exec()) # 4.进入程序的主循环并通过exit()函数确保主循环安全结束