多线程在PyQt5中的应用记录

╰+攻爆jí腚メ 2023-01-20 14:57 18阅读 0赞

写在前面

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

理解多线程

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

单线程执行两个按钮

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

  1. from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
  2. import sys
  3. import time
  4. class Demo(QWidget):
  5. def __init__(self):
  6. super().__init__()
  7. self.btn1 = QPushButton("按钮1", self)
  8. self.btn1.move(120, 80)
  9. self.btn1.clicked.connect(self.click1)
  10. self.btn2 = QPushButton("按钮2", self)
  11. self.btn2.move(120, 120)
  12. self.btn2.clicked.connect(self.click2)
  13. def click1(self):
  14. values = [1, 2, 3, 4, 5]
  15. for i in values:
  16. print(i)
  17. time.sleep(1)
  18. def click2(self):
  19. values = ["a", "b", "c", "d", "e"]
  20. for i in values:
  21. print(i)
  22. time.sleep(1)
  23. if __name__ == "__main__":
  24. app = QApplication(sys.argv)
  25. mywin = Demo()
  26. mywin.show()
  27. sys.exit(app.exec_())

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

多线程任务

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

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu May 28 14:20:25 2020
  4. @author: HUANG Gang
  5. """
  6. from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
  7. import sys
  8. import time
  9. class Thread1(QThread):
  10. def __init__(self):
  11. super().__init__()
  12. def run(self):
  13. values = [1, 2, 3, 4, 5]
  14. for i in values:
  15. print(i)
  16. time.sleep(1)
  17. class Thread2(QThread):
  18. def __init__(self):
  19. super().__init__()
  20. def run(self):
  21. values = ["a", "b", "c", "d", "e"]
  22. for i in values:
  23. print(i)
  24. time.sleep(1)
  25. class Demo(QWidget):
  26. def __init__(self):
  27. super().__init__()
  28. self.btn1 = QPushButton("按钮1", self)
  29. self.btn1.move(120, 80)
  30. self.btn1.clicked.connect(self.click1)
  31. self.btn2 = QPushButton("按钮2", self)
  32. self.btn2.move(120, 120)
  33. self.btn2.clicked.connect(self.click2)
  34. def click1(self):
  35. self.thread1 = Thread1()
  36. self.thread1.start()
  37. def click2(self):
  38. self.thread2 = Thread2()
  39. self.thread2.start()
  40. if __name__ == "__main__":
  41. app = QApplication(sys.argv)
  42. mywin = Demo()
  43. mywin.show()
  44. sys.exit(app.exec_())

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

线程锁

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

  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Thu May 28 14:20:25 2020
  4. @author: HUANG Gang
  5. """
  6. from PyQt5.Qt import (QApplication, QWidget, QPushButton, QThread, QMutex, pyqtSignal)
  7. import sys
  8. import time
  9. qmut1 = QMutex()
  10. qmut2 = QMutex()
  11. class Thread1(QThread):
  12. def __init__(self):
  13. super().__init__()
  14. def run(self):
  15. qmut1.lock() # 线程加锁
  16. values = [1, 2, 3, 4, 5]
  17. for i in values:
  18. print(i)
  19. time.sleep(1)
  20. qmut1.unlock() # 线程解锁
  21. class Thread2(QThread):
  22. def __init__(self):
  23. super().__init__()
  24. def run(self):
  25. qmut2.lock() # 线程加锁
  26. values = ["a", "b", "c", "d", "e"]
  27. for i in values:
  28. print(i)
  29. time.sleep(1)
  30. qmut2.unlock() # 线程解锁
  31. class Demo(QWidget):
  32. def __init__(self):
  33. super().__init__()
  34. self.btn1 = QPushButton("按钮1", self)
  35. self.btn1.move(120, 80)
  36. self.btn1.clicked.connect(self.click1)
  37. self.btn2 = QPushButton("按钮2", self)
  38. self.btn2.move(120, 120)
  39. self.btn2.clicked.connect(self.click2)
  40. def click1(self):
  41. self.thread1 = Thread1()
  42. self.thread1.start()
  43. def click2(self):
  44. self.thread2 = Thread2()
  45. self.thread2.start()
  46. if __name__ == "__main__":
  47. app = QApplication(sys.argv)
  48. mywin = Demo()
  49. mywin.show()
  50. sys.exit(app.exec_())

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

发表评论

表情:
评论列表 (有 0 条评论,18人围观)

还没有评论,来说两句吧...

相关阅读

    相关 PyQt5篇】线

    这是因为QThread类提供了一个事件循环,可以在后台处理线程的任务,而不会影响到线程的响应性。,当在主线程中执行耗时操作时(例如click_1方法中的for循环),它。...