1.Event事件的作用:
用来控制线程的执行,由一些线程去控制另一些线程。
from threading import Event,Thread import time #调用Event类实例化一个对象 e=Event() #若该方法出现在任务中,则为False,阻塞 # e.wait() #False #若该方法出现在任务中,则将其他线程中的False改为True,进入就绪态与运行态。 # e.set() #True def light(): print('红灯亮。。') time.sleep(5) #应该开始发送信号,告诉其他线程准备执行。 e.set() print('绿灯亮。。') def car(name): print('正在等红灯。。。') #让所有汽车任务进入阻塞态 e.wait() print(f'{name}正在加速漂移。。') #让一个light线程任务,控制多个car线程任务 t=Thread(target=light) t.start() for line in range(10): t=Thread(target=car,args=(f'童子军jason{line}号', )) t.start() 运行结果: 红灯亮。。 正在等红灯。。。 正在等红灯。。。 . . 绿灯亮。。 童子军jason0号正在加速漂移。。 童子军jason1号正在加速漂移。。 童子军jason2号正在加速漂移。。 . .
2.进程池与线程池。
1)什么是进程池与线程池?
进程池与线程池是用来控制当前程序允许创建(进程/线程)的数量。
2)进程池与线程池的作用
保证在硬件允许的范围内创建(进程/线程)的数量。
3)如何使用:
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time # ProcessPoolExecutor(5) #5代表只能开启5个进程 # ProcessPoolExecutor() #默认以CPU的个数限制进程数 pool=ThreadPoolExecutor(5) #5代表只能开启5个线程 # ThreadPoolExecutor() #默认以CPU个数*5 限制线程数 #t=Thread() #异步提交,并发操作 # t.start() # pool.submit('传函数地址') #异步提交任务 # def task(): # print('线程任务开始了。。') # time.sleep(2) # print('线程结束了。。') # # for line in range(5): # pool.submit(task) #异步提交任务 # pool.submit('传函数地址').add_done_callback('回调函数地址') def task(res): print('线程任务开始了。。') time.sleep(2) print('线程结束了。。') return 222 #回调函数 def call_back(res): print(type(res)) #注意:赋值操作不要与接收的res同名 res2=res.result() print(res2) for line in range(5): pool.submit(task,1).add_done_callback(call_back) #会让所有线程池的任务结束后,才往下执行代码 pool.shutdown() print('hello') 运行结果: 线程任务开始了。。 线程任务开始了。。 线程任务开始了。。 线程任务开始了。。 线程任务开始了。。 线程结束了。。 <class 'concurrent.futures._base.Future'> 222 线程结束了。。 <class 'concurrent.futures._base.Future'> 222 . . 线程结束了。。 <class 'concurrent.futures._base.Future'> 222 hello
3.协程
--进程:资源单位
--线程:执行单位
-协程:在单线程下实现并发
注意:协程不是操作系统资源,他是程序起的名字,为让单线程能实现并发。
协程的目的:
--操作系统:多道技术,切换+保存状态
1)遇到IO
2)CPU执行时间过长
-协程:通过手动模拟操作系统“多道技术”,实现 切换+保存状态。
1)手动实现 遇到IO切换,欺骗操作系统误以为没有IO操作。
-单线程 遇到IO,切换+保存状态。
-单线程 计算密集型,来回切换+保存状态,反而效率更低。
优点:在IO密集型下,会提高效率。
缺点:若在计算密集型的情况下,来回切换,反而效率更低。
-如何实现协程:切换+保存状态
-yield:保存状态
-并发:切换
from gevent import monkey,spawn,joinall monkey.patch_all() #可以监听该程序下所有的IO操作 import time def func1(): print('1') #IO操作 time.sleep(1) def func2(): print('2') #IO操作 time.sleep(3) def func3(): print('3') #IO操作 time.sleep(5) start_time=time.time() s1=spawn(func1) s2=spawn(func2) s3=spawn(func3) # s2.join() #发送信号,等待别的线程,单线程下就是等待自己 # s1.join() # s3.join() #括号内的数必须转序列类型,数列类型有元组或者列表 joinall([s1,s2,s3]) end_time=time.time() print(end_time-start_time) 运行结果: 1 2 3 5.038288116455078
4.同步批量爬取梨视频
from concurrent.futures import ThreadPoolExecutor import requests import re import uuid pool=ThreadPoolExecutor(200) #1.发送请求函数 def get_page(url): response=requests.get(url) return response #2.解析主页获取视频id号 response=res.result() movie_detail_url=re.findall('srcUrl="(.*?)"',response.text,re.S)[0] print(f'往视频链接: {movie_detail_url}发送请求...') # 异步往视频详情页链接发送请求 pool.submit(get_page, movie_detail_url).add_done_callback(save_movie) return movie_detail_url # 4.往真实视频链接发送请求,获取数据并保存到本地 def save_movie(res): movie_response = res.result() name = str(uuid.uuid4()) print(f'{name}.mp4视频开始保存...') with open(f'{name}.mp4', 'wb') as f: f.write(movie_response.content) print('视频下载完毕!') if __name__ == '__main__': # 1.访问主页获取数据 index_response = get_page('https://www.pearvideo.com/') # # 2.解析主页获取所有的视频id号 id_list = parse_index(index_response) print(id_list) # 3.循环对每个视频详情页链接进行拼接 for id in id_list: print(id) detail_url = 'https://www.pearvideo.com/video_' + id # 异步提交爬取视频详情页,把返回的数据,交给parse_detail(回调函数) pool.submit(get_page, detail_url).add_done_callback(parse_detail)
5.TCP服务端实现单线程下并发
#服务端 from gevent import monkey,spawn,socket # monkey.patch_all() #监听该程序下的所有IO操作 # import socket server=socket.socket() server.bind( ('127.0.0.1',9529) ) server.listen(5) print('服务端启动') #线程任务,执行接收客户端消息与发送消息给客户端 def working(conn): while True: try: data=conn.recv(1024).decode('utf8') if len(data)==0: break print(data) conn.send(data.upper().encode('utf8')) except Exception as e: print(e) break conn.close() def serv(): while True: conn,addr=server.accept() spawn(working,conn) if __name__ == '__main__': s1=spawn(serv) s1.join()
import socket from threading import Thread,current_thread def client(): client=socket.socket() client.connect( ('127.0.0.1',9529) ) print('客户端启动') number=0 while True: send_data=f'{current_thread().name} {number}' client.send(send_data.encode('utf8')) data=client.recv(1024).decode('utf8') print(data) number+=1 #模拟了300个用户去并发访问服务端 for i in range(300): t=Thread(target=client) t.start()