什么是协程(第三方模块gevent--内置模块asyncio)
目录
一:协程
1.什么是协程?
copypython的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行) 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)
2.协程的作用?
copy程序级别的切换,提升效率 单线程下实现并发 让原本是CPU切换线程 变成 程序切换线程
3.安装第三方模块:在命令行下
copypip3 install greenlet pip3 uninstall greenlet 卸载第三方模块 pip3 list # 列出当前解释器环境下安装的第三方模块
二:greenlet模块(初级模块,实现了保存状态加切换)
copy首先 先引入的是greenlet模块 然后在引入真正的协程模块 greenlet模块 只是初级模块,只实现了保存状态加切换,没实现遇到io自动切换
- 代码实现
copyfrom greenlet import greenlet def eat(name): print(name,'在吃了一口') g2.switch(name) print(name,'在吃了第二口') # 执行g2,不需要再传参数了,因为已经传过了,从玩了第二下开始执行 g2.switch() def play(name): print(name, '玩了一下') # 执行g1,不需要再传参数了,因为已经传过了,从第二口开始执行 g1.switch() print(name, '玩了第二下') # 类加括号实列化 得到对象 g1=greenlet(eat) g2=greenlet(play) # 执行 g1.switch('egon')
三: gevent模块(协程模块)
copygevent模块,协程模块,遇到io可以自动切换
- 代码实现
copyimport gevent import time def eat(name): print(name, '在吃了一口') # 遇到了io 自动切换 # 只能用gevent.sleep gevent.sleep(2) print(name, '在吃了第二口') def play(name): print(name, '玩了一下') # 遇到了io,是gevent的io gevent.sleep(3) print(name, '玩了第二下') # 执行前时间 ctime = time.time() # 内置有返回值 设置协程任务,执行,函数(参数) res1 = gevent.spawn(eat, 'egon') res2 = gevent.spawn(play, 'egon') # # 等待协程执行完,在执行主线程 # res1.join() # res2.join() gevent.joinall([res1, res2]) # 相当于上面两句 print('主线程') print(time.time()-ctime)
- 输出结果
copyegon 在吃了一口 egon 玩了一下 egon 在吃了第二口 egon 玩了第二下 主线程 3.0269923210144043
1.time 模式协程 遇到io情况
copy使用原来的time的io,不会切换,并且变成了串行
- 代码实现
copyimport gevent import time def eat(name): print(name, '在吃了一口') # 遇到了io 自动切换 # 只能用gevent.sleep time.sleep(2) print(name, '在吃了第二口') def play(name): print(name, '玩了一下') # 遇到了io,是gevent的io time.sleep(3) print(name, '玩了第二下') # 执行前时间 ctime = time.time() # 内置有返回值 设置协程任务,执行,函数(参数) res1 = gevent.spawn(eat, 'egon') res2 = gevent.spawn(play, 'egon') # # 等待协程执行完,在执行主线程 # res1.join() # res2.join() gevent.joinall([res1, res2]) # 相当于上面两句 print('主线程') print(time.time()-ctime)
2.解决time无法遇到io切换
- time无法io切换原因:
copy线程内切换 用原来的io,会出现它的Gil锁立马就释放了,就切换到另一条线程上执行了,不会再执行异步,而是串行
- 解决方法:
copy猴子补丁 : 把原本的io全部替换成gevent的io
- 猴子补丁作用:
copy将原本的IO 全部换成不释放Gil锁的IO 所有需要把Gil锁重写一遍 达到遇到io自动切换
- 代码实现
copy# 猴子补丁:把原来的io全都替换成gevent的io from gevent import monkey;monkey.patch_all() import gevent import time def eat(name): print(name, '在吃了一口') # 遇到了io 自动切换 # 只能用gevent.sleep time.sleep(2) print(name, '在吃了第二口') def play(name): print(name, '玩了一下') # 遇到了io,是gevent的io time.sleep(3) print(name, '玩了第二下') # 执行前时间 ctime = time.time() # 内置有返回值 设置协程任务,执行,函数(参数) res1 = gevent.spawn(eat, 'egon') res2 = gevent.spawn(play, 'egon') # # 等待协程执行完,在执行主线程 # res1.join() # res2.join() gevent.joinall([res1, res2]) # 相当于上面两句 print('主线程') print(time.time()-ctime)
四:协程实现TCP服务端并发的效果
copy并发效果:一个服务端可以同时服务多个客户端 实现: 多进程下开设多线程 多线程下开设协程
- 服务器
copyimport socket from gevent import monkey;monkey.patch_all() from gevent import spawn def talk(sock): while True: try: # 固定接收客户端数据 data = sock.recv(1024) # 判断接收数据是否为0 if data == 0: break print(data) # 向客户端发送数据 sock.send(data + b'hello baby!') # 异常捕获 except ConnectionResetError as e: print(e) # 接收套字节 sock.close() break def servers(): # 套字节 server = socket.socket() # 绑定ip与端口 server.bind(('127.0.0.1', 8080)) # 连接池 server.listen() # 通信循环 while True: # 被动客户端连接 并返回 sock 通信 接收与发送 # addr 客户端地址 sock, addr = server.accept() # 执行任务 函数(sock)参数 spawn(talk, sock) # 执行封装 g1 = spawn(servers) g1.join()
- 客户端
copy# 客户端开设几百个线程发消息即可 from threading import Thread, current_thread from socket import * def client(): # UDP协议 client = socket(AF_INET, SOCK_STREAM) # 连接服务器ip与port client.connect(('127.0.0.1', 8080)) n = 0 while True: # 子线程名子 循环加1 msg = '%s say hello %s' % (current_thread().name, n) n += 1 # 发送数据 编码 client.send(msg.encode('utf-8')) # 固定接收数据 data = client.recv(1024) # 解码 print(data.decode('utf-8')) if __name__ == '__main__': # 循环执行500个线程 for i in range(500): # 设置任务 t = Thread(target=client) # 执行任务 t.start()
五: asyncio(内置协程模块)
copy内置模块 python 3.4 推出这个模块,python作者主导的
copyimport asyncio import time import threading # 这个函数是协程函数 async def task(): # 获取当前线程的名字 res=threading.current_thread().getName() print(res) print('xxx') # 只要有IO操作,前面加个await await asyncio.sleep(2) print('协程执行完成') async def task2(): # 获取当前线程的名字 res=threading.current_thread().getName() print(res) print('2222') await asyncio.sleep(3) print('222协程执行完成') ctime=time.time() # 拿到一个循环对象 loop=asyncio.get_event_loop() # 设置任务 tasks=[task(),task2()] # 执行任务 loop.run_until_complete(asyncio.wait(tasks)) loop.close() print(time.time()-ctime)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步