多线程在PyQt5中的应用记录

写在前面

博主在学习Python编程过程中,有个问题一直没有理解透彻,就是多线程问题,因为工作中的项目都比较小,很少用到多线程,但是这个问题却是个很底层的问题,因此还是打算学习一下,查阅了一些资料,记录一下学习心得。

理解多线程

一般情况下,应用程序都是单线程运行的,但是对于GUI程序,可能一个线程无法满足要求,比如我有两个按钮A和B,我点击了一个按钮A,这个按钮的工作量很大,需要运行很长时间,然后我就把这个按钮的工作任务放到线程里面去,让它后台工作,这个时候,再点击另外一个按钮B,可以让这个按钮B开始工作。
先看看单线程的工作机制。

单线程执行两个按钮

在界面上设置按钮1和按钮2,按钮1打印数字1、2、3、4、5,按钮2打印a、b、c、d、e,我们大概率会这么写

from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
import sys
import time


class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.btn1 = QPushButton("按钮1", self)
        self.btn1.move(120, 80)
        self.btn1.clicked.connect(self.click1)

        self.btn2 = QPushButton("按钮2", self)
        self.btn2.move(120, 120)
        self.btn2.clicked.connect(self.click2)
	
	def click1(self):
		values = [1, 2, 3, 4, 5]
		for i in values:
			print(i)
			time.sleep(1)

	def click2(self):
		values = ["a", "b", "c", "d", "e"]
		for i in values:
			print(i)
			time.sleep(1)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    mywin = Demo()
    mywin.show()
    sys.exit(app.exec_())

看看运行情况
在这里插入图片描述
如果我点击按钮1后马上又点击按钮2,程序会出现假死的情况,必须得等按钮1的任务执行完才能开始按钮2的任务,这样也挺浪费资源的,所以这里考虑用多线程来解决这个问题。

多线程任务

现在考虑把这两个按钮的工作都放到线程里面去,来看看具体是怎么实施的,先上代码

# -*- coding: utf-8 -*-
"""
Created on Thu May 28 14:20:25 2020

@author: HUANG Gang
"""
from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
import sys
import time


class Thread1(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        values = [1, 2, 3, 4, 5]
        for i in values:
            print(i)
            time.sleep(1)


class Thread2(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        values = ["a", "b", "c", "d", "e"]
        for i in values:
            print(i)
            time.sleep(1)


class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.btn1 = QPushButton("按钮1", self)
        self.btn1.move(120, 80)
        self.btn1.clicked.connect(self.click1)

        self.btn2 = QPushButton("按钮2", self)
        self.btn2.move(120, 120)
        self.btn2.clicked.connect(self.click2)

    def click1(self):
        self.thread1 = Thread1()
        self.thread1.start()

    def click2(self):
        self.thread2 = Thread2()
        self.thread2.start()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mywin = Demo()
    mywin.show()
    sys.exit(app.exec_())

在创建应用程序之前,我把这两个按钮的工作任务写到两个独立的线程中去了,然后在界面程序里面实例化这两个线程,来看看执行情况
在这里插入图片描述
注意控制台的输出,在加入了这两个线程后,并未出现卡死的情况,线程之间不会相互干扰,但是有个问题,如果按钮1的线程任务完成之前重新点击了按钮1,那么这两个指令之间会产生干扰,就是按钮1的任务会刷新。这不是我希望发生的事情。
解决这个问题要用到线程锁。

线程锁

线程锁相对来说容易理解一点,就是在线程开始工作时,加上一把锁,在任务结束后,方可解锁,这样就可以保证整个任务是连续的,看代码

# -*- coding: utf-8 -*-
"""
Created on Thu May 28 14:20:25 2020

@author: HUANG Gang
"""
from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
import sys
import time

qmut1 = QMutex()
qmut2 = QMutex()


class Thread1(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        qmut1.lock()  # 线程加锁
        values = [1, 2, 3, 4, 5]
        for i in values:
            print(i)
            time.sleep(1)
        qmut1.unlock()  # 线程解锁


class Thread2(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        qmut2.lock()  # 线程加锁
        values = ["a", "b", "c", "d", "e"]
        for i in values:
            print(i)
            time.sleep(1)
        qmut2.unlock()  # 线程解锁


class Demo(QWidget):
    def __init__(self):
        super().__init__()
        self.btn1 = QPushButton("按钮1", self)
        self.btn1.move(120, 80)
        self.btn1.clicked.connect(self.click1)

        self.btn2 = QPushButton("按钮2", self)
        self.btn2.move(120, 120)
        self.btn2.clicked.connect(self.click2)

    def click1(self):
        self.thread1 = Thread1()
        self.thread1.start()

    def click2(self):
        self.thread2 = Thread2()
        self.thread2.start()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mywin = Demo()
    mywin.show()
    sys.exit(app.exec_())

在程序开始执行之前,先实例化两个线程锁,这两把锁分别加在两个线程之中,看看程序执行情况。
在这里插入图片描述
看看控制台的输出,1/2/3/4/5和a/b/c/d/e的输出是相对连续的,当然两个按钮之间还是会相互交叉打印,这也是多线程的精髓。
至此,我大概理解了多线程的使用方法,后面就可以进行更复杂的应用了。

猜你喜欢

转载自blog.csdn.net/u012848304/article/details/116755209
今日推荐