企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] <br> ### 线程的形象描述 在计算机运算中,我们会接触到`进程`与`线程`两个概念。 **进程:**如果我们同时打开QQ和微信,他们是在不同的进程中运行,他们数据不会共享。 **线程:**我们在播放视频时,播放器一边在播放页面,一边在播放声音,他们是同时并且同步进行的,他们数据共享,这就是在同一个进程中的两个线程。 ### 单线程与多线程实例感受 假设:loop0() 执行需要4s,loop1()执行需要2s。 - 在`单线程`中,loop0与loop1`串行执行`,先完成loop0,再执行loop1.总共花费了4s+2s=6s ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from time import sleep import time def loop0(): print("loop 0 start...") sleep(4) # 模拟休息4s print("loop 0 done!") def loop1(): print("loop 1 start...") sleep(2) # 模拟休息2s print("loop 1 done!") if __name__ == '__main__': start=time.time() # 单线程,先执行loop0,loop0执行结束后再执行loop1 loop0() loop1() print("total time:%s" % (time.time()-start)) ``` 运行结果如: ```cmd loop 0 start... loop 0 done! loop 1 start... loop 1 done! total time:6.001265048980713 ``` <br> - 在`多线程`中,loop0与loop1会`并行执行`,总共花的时间为最长线程时间4s ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time def loop0(): print("loop 0 start...") time.sleep(4) # 模拟休息4s print("loop 0 done!") def loop1(): print("loop 1 start...") time.sleep(2) # 模拟休息2s print("loop 1 done!") if __name__ == '__main__': start=time.time() # 创建两个线程 t1 = threading.Thread(target=loop0,args=()) t2 = threading.Thread(target=loop1,args=()) # 线程开始 t1.start() t2.start() # 线程等候 t1.join() t2.join() print("total time:%s" % (time.time()-start)) ``` 运行结果: ```cmd loop 0 start... loop 1 start... loop 1 done! loop 0 done! total time:4.009899854660034 ``` --- ### 快速掌握线程的写法 #### 使用threading模块的Thread类创建线程 竟然有3种方式创建线程,我们能够熟练掌握其中一种就好。。。 ##### 方式一:创建一个Thread的实例,传给它一个函数 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time def watching_tv(tv_name): """看电视""" for i in range(5): time.sleep(1) print("watching tv [%s] part[%s]..." %(tv_name,i)) def listening_music(music_name): """听音乐""" for i in range(5): time.sleep(1.5) print("listening music [%s] part[%s]" %(music_name,i)) if __name__ == '__main__': # 创建两个线程,一边看电视,一边听音乐 print("The main program start...") # 创建一个Thread实例,传给它一个函数 t1 = threading.Thread(target=watching_tv,args=("杀破狼2",)) t2 = threading.Thread(target=listening_music,args=("爱如潮水",)) # 开始线程的执行 t1.start() t2.start() # 程序挂起,直到该子线程结束再继续执行主线程; t1.join() t2.join() print("The main program done!") ``` ##### 方式二:创建一个Thread的实例,传给它一个可调用的类对象 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time class Relex(object): def __init__(self,func,*args): self.func = func self.args = args def __call__(self): self.func(*self.args) def watching_tv(tv_name): """看电视""" for i in range(5): time.sleep(1) print("watching tv [%s] part[%s]..." %(tv_name,i)) def listening_music(music_name): """听音乐""" for i in range(5): time.sleep(1.5) print("listening music [%s] part[%s]" %(music_name,i)) if __name__ == '__main__': print("The main program start...") # 创建两个可调用类对象 cal_obj_1 = Relex(watching_tv,"杀破狼2") cal_obj_2 = Relex(listening_music,"爱如潮水") # 创建一个Thread实例,传给它一个可调用类对象 t1 = threading.Thread(target=cal_obj_1) t2 = threading.Thread(target=cal_obj_2) # 开始线程的执行 t1.start() t2.start() # 程序挂起,直到该子线程结束再继续执行主线程; t1.join() t2.join() print("The main program done!") ``` ##### 方式三:创建threading.Thread 的子类,重写\__init__()与run()方法(通用) ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import threading import time class Relex(threading.Thread): def __init__(self,func,*args): super(Relex,self).__init__() self.func = func self.args = args def run(self): self.func(*self.args) def watching_tv(tv_name): """看电视""" for i in range(5): time.sleep(1) print("watching tv [%s] part[%s]..." %(tv_name,i)) def listening_music(music_name): """听音乐""" for i in range(5): time.sleep(1.5) print("listening music [%s] part[%s]" %(music_name,i)) if __name__ == '__main__': print("The main program start...") # 创建两个继承了thread.Thread类,并且重写了run方法的子类。 t1 = Relex(watching_tv,"杀破狼2") t2 = Relex(listening_music,"爱如潮水") # 开始线程的执行 t1.start() t2.start() # 程序挂起,直到该子线程结束再继续执行主线程; t1.join() t2.join() print("The main program done!") ``` 以上三个实例,运行的结果都是一样的并行运行,运行结果如下: ```cmd The main program start... watching tv [杀破狼2] part[0]... listening music [爱如潮水] part[0] watching tv [杀破狼2] part[1]... listening music [爱如潮水] part[1] watching tv [杀破狼2] part[2]... watching tv [杀破狼2] part[3]... listening music [爱如潮水] part[2] watching tv [杀破狼2] part[4]... listening music [爱如潮水] part[3] listening music [爱如潮水] part[4] The main program done! ``` --- #### 多线程通过消息队列queue交换信息 大家有没有听说过生产者与消费者模型?举个厨师与顾客的例子:厨房中有两个厨师,不停的在生产包子,四个顾客在餐厅中不停的吃包子。包子一边在生产,一边在被消耗,这个就是生产者与消费者模型了。 在这一小节,我们通过多线程与消息队列来模拟这样一个场景。 ![](https://box.kancloud.cn/7323a5966fef0e1deeab34367f1867ca_575x366.jpg) queue是有顺序的,FIFO(先进先出),我们可以在脑中想象成它就是一个管道,厨师在左边塞包子,顾客从右边拿包子。 用代码来解释: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import threading, time import queue # 导入消息队列模块 import random # 导入随机数模块,是为了模拟生产者与消费者速度不一致的情形 q = queue.Queue() # 实例化一个对象 def Producer(name): # 生产者函数 # 假设一个厨师只生产5个包子 for i in range(1,6): q.put("%s_%s"%(name,i)) # 将结果放入消息队列中 print("生产者 %s 生产了一个包子[%s_%s],现在总包子数:%s" % (name,name,i,q.qsize())) time.sleep(random.randrange(2)) # 随机每生产一个包子的速度 def Consumer(name): # 消费者函数 # 只要还有包子,顾客就一直吃吃吃... while True: data = q.get() # 取用消息队列中存放的结果 print("消费者 %s 消费了一个包子[%s],包子剩余:%s" % (name, data, q.qsize())) time.sleep(random.randrange(1, 3)) # 随机每吃一个包子的速度 if __name__ == '__main__': # 创建两个厨师生产包子的线程 p1 = threading.Thread(target=Producer, args=('Milton',)) p2 = threading.Thread(target=Producer, args=('Cherish',)) # 创建两个顾客吃包子的线程 c1 = threading.Thread(target=Consumer, args=('张三',)) c2 = threading.Thread(target=Consumer, args=('李四',)) # 线程运行 p1.start() p2.start() c1.start() c2.start() ``` 运行结果,如: ```cmd 生产者 Milton 生产了一个包子[Milton_1],现在总包子数:1 生产者 Cherish 生产了一个包子[Cherish_1],现在总包子数:2 消费者 张三 消费了一个包子[Milton_1],包子剩余:1 消费者 李四 消费了一个包子[Cherish_1],包子剩余:0 生产者 Cherish 生产了一个包子[Cherish_2],现在总包子数:1 生产者 Milton 生产了一个包子[Milton_2],现在总包子数:2 生产者 Milton 生产了一个包子[Milton_3],现在总包子数:3 生产者 Milton 生产了一个包子[Milton_4],现在总包子数:4 生产者 Milton 生产了一个包子[Milton_5],现在总包子数:5 消费者 张三 消费了一个包子[Cherish_2],包子剩余:4 消费者 李四 消费了一个包子[Milton_2],包子剩余:3 生产者 Cherish 生产了一个包子[Cherish_3],现在总包子数:4 生产者 Cherish 生产了一个包子[Cherish_4],现在总包子数:5 消费者 张三 消费了一个包子[Milton_3],包子剩余:4 消费者 李四 消费了一个包子[Milton_4],包子剩余:3 生产者 Cherish 生产了一个包子[Cherish_5],现在总包子数:4 消费者 李四 消费了一个包子[Milton_5],包子剩余:3 消费者 张三 消费了一个包子[Cherish_3],包子剩余:2 消费者 张三 消费了一个包子[Cherish_4],包子剩余:1 消费者 李四 消费了一个包子[Cherish_5],包子剩余:0 ``` <hr style="margin-top:100px"> :-: ![](https://box.kancloud.cn/2ff0bc02ec938fef8b6dd7b7f16ee11d_258x258.jpg) ***微信扫一扫,关注“python测试开发圈”,了解更多测试教程!***