多线程的理解(python和pyQt)
概念
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理。
- 用户界面可以更加吸引人,比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。 程序的运行速度可能加快。
- 在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程可以分为
- 内核线程:由操作系统内核创建和撤销。
- 用户线程:不需要内核支持而在用户程序中实现的线程。
Python3 线程中常用的两个模块为
-
_thread
-
threading(推荐使用)
thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 “_thread”。
线程模块
threading 模块除了包含 _thread 模块中的所有方法外,还提供的其他方法:
- threading.currentThread(): 返回当前的线程变量。
- threading.enumerate():
返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 - threading.activeCount():
返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
除了使用方法外,线程模块同样提供了Thread类来处理线程,Thread类提供了以下方法:
- run(): 用以表示线程活动的方法。
- start():启动线程活动。
- join([time]): 等待至线程中止。线程结束之前,join()后续的操作不会进行。这阻塞调用线程直至线程的join()
方法被调用中止-正常退出或者抛出未处理的异常-或者是可选的超时发生。 - isAlive(): 返回线程是否活动的。
- getName(): 返回线程名。
- setName(): 设置线程名。
通过直接从 threading.Thread 继承创建一个新的子类,并实例化后调用 start() 方法启动新线程,即它调用了线程的 run() 方法
import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("开始线程:" + self.name) print_time(self.name, self.counter, 5) print ("退出线程:" + self.name) def print_time(threadName, delay, counter): while counter: if exitFlag: threadName.exit() time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 # 创建新线程 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # 开启新线程 thread1.start() thread2.start() thread1.join() thread2.join() print ("退出主线程")
输出:
线程同步
如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步。
使用 Thread 对象的 Lock 和 Rlock 可以实现简单的线程同步,这两个对象都有 acquire 方法和 release 方法,对于那些需要每次只允许一个线程操作的数据,可以将其操作放到 acquire 和 release 方法之间。
考虑这样一种情况:一个列表里所有元素都是0,线程"set"从后向前把所有元素改成1,而线程"print"负责从前往后读取列表并打印。
那么,可能线程"set"开始改的时候,线程"print"便来打印列表了,输出就成了一半0一半1,这就是数据的不同步。为了避免这种情况,引入了锁的概念。
锁有两种状态——锁定和未锁定。每当一个线程比如"set"要访问共享数据时,必须先获得锁定;如果已经有别的线程比如"print"获得锁定了,那么就让线程"set"暂停,也就是同步阻塞;等到线程"print"访问完毕,释放锁以后,再让线程"set"继续。
经过这样的处理,打印列表时要么全部输出0,要么全部输出1,不会再出现一半0一半1的尴尬场面。
import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("开启线程: " + self.name) # 获取锁,用于线程同步 threadLock.acquire() print_time(self.name, self.counter, 3) # 释放锁,开启下一个线程 threadLock.release() def print_time(threadName, delay, counter): while counter: time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 threadLock = threading.Lock() threads = [] # 创建新线程 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # 开启新线程 thread1.start() thread2.start() # 添加线程到线程列表 threads.append(thread1) threads.append(thread2) # 等待所有线程完成 for t in threads: t.join() print ("退出主线程")
输出:
pyQt线程
通常,我们会通过继承QThread,重写run方法,来实现业务需求
class MBThread(QThread): oneSecondTriger = pyqtSignal() def __init__(self): super(MBThread,self).__init__() def run(self): while True: self.oneSecondTriger.emit() time.sleep(1)
我定义了一个MBThread类,继承自QThread,在run函数内,每隔1s触发一个信号
而在另一个类MyWindow中,我定义了MBThread的实例,并绑定了槽函数。代码如下:
class MyWindow(QMainWindow,Ui_MainWindow): def __init__(self): super(MyWindow,self).__init__() self.timeThread = MBThread() self.timeThread.oneSecondTriger.connect(self.timeUpdate) self.timeThread.start() def timeUpdate(self): self.label_Time.setText(time.strftime('%H:%M:%S'))
为了方便阅读,上段代码做了一些删除,能表达清楚意思即可。槽函数timeUpdate在新的类中MyWindow定义,每次信号触发都将调用该函数。同样地,UI当中的时间标签label_Time也会在每次触发调用后,更新界面时间。
多余的话:上述代码来自我的一个小项目,应用了Python的Modbus库,在线程槽函数timeUpdate内,改成你需要的业务处理就行了。
pyQt线程锁
#锁的使用 #创建锁 mutex = threading.Lock() #锁定 mutex.acquire([timeout]) #释放 mutex.release()
————————————————
感谢以下博主:
原文链接:https://blog.csdn.net/ysgjiangsu/article/details/84671357
- 深入理解 Python 中的多线程 新手必看
- python 多线程中threading的join()与setDaemon()的一些理解
- 【好文收藏】理解python多线程
- python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器实例
- 理解python多线程(python多线程简明教程)
- 深入理解python多线程与GIL
- 彻底理解Python多线程中的setDaemon与join【配有GIF示意】
- 理解一下Python中的多线程,多进程,多协程
- 理解python多线程(python多线程简明教程)
- python异常机制、多进程与PyQt5中的QTimer、多线程
- 关于Python多线程的理解
- 理解 Python 中的多线程
- python 多线程的理解
- python3+PyQt5 实现理解python语法并做高亮显示的纯文本编辑器
- 通过python threading Thread理解多线程和单线程的运行机制
- Python的多线程理解,转自虫师https://www.cnblogs.com/fnng/p/3670789.html
- python的多线程简单实例理解
- 怎么样理解python中的GIL,与多线程的关系。
- python3+PyQt5 创建多线程网络应用-TCP客户端和TCP服务器
- Python多线程的创建,相关函数和守护线程的理解