协程(gevent、asyncio)
协程
-
协程是操作系统不可见的
-
什么是协程?
- 协程本质上就是一条线程,多个任务在一条线程上来回切换,来规避IO操作,就达到了我们将一条线程中的IO操作降到最低的目的。
gevent
-
第三方模块
- gevent,利用了底层模块(greelet)完成切换 + 自动规避IO的功能
- gevent模块不能规避所有IO,只能规避一部分,查看方法:
- patch_all()之前打印一次,patch_all()之后打印一次,如果不一样则规避了,如果一样则没有规避
- 直接通过按住ctrl点击patch_all()查看源码为True的可以规避,为Flase的不能规避
-
代码:
-
#方式一: import gevent def func(): #所有带有IO操作的内容都写在函数里,然后提交function print('start func') gevent.sleep(1) print('end func') g1=gevent.spawn(func) g2=gevent.spawn(func) g3=gevent.spawn(func) # gevent.sleep(2) # g1.join() #一直阻塞,直到协程g1任务执行结束 # g2.join() # g3.join() gevent.joinall([g1,g2,g3]) #方式二 ********* from gevent import monkey monkey.patch_all() #此两句必须写在所有导入模块的最上面,否则报错 import gevent import time def func(): #所有带有IO操作的内容都写在函数里,然后提交function print('start func') time.sleep(1) print('end func') g1=gevent.spawn(func) g2=gevent.spawn(func) g3=gevent.spawn(func) time.sleep(2) # g1.join() #一直阻塞,直到协程g1任务执行结束 # g2.join() # g3.join() # gevent.joinall([g1,g2,g3])
-
-
基于gevent协程实现Socket的并发
-
server
from gevent import monkey monkey.patch_all() import gevent import socket sk=socket.socket() sk.bind(('127.0.0.1',1235)) sk.listen() def func(conn): while True: msg=conn.recv(1024).decode('utf-8') MSG=msg.upper() conn.send(MSG.encode('utf-8')) while True: conn,addr=sk.accept() gevent.spawn(func,conn) #spawn开启任务
-
client
import socket sk=socket.socket() sk.connect(('127.0.0.1',1235)) while True: ret=input('请输入:') sk.send(ret.encode('utf-8')) mag=sk.recv(1024).decode('utf-8') print(mag)
-
asyncio
-
原生的内置模块
-
asyncio,利用了底层模块(yield)完成切换 + 自动规避IO的功能
-
aiohttp模块:基于asyncio实现的,用来做并发的爬虫
-
sanic异步的轻量级的Web框架中,基于asyncio实现的
- async:函数前面有async就代表是一个协程函数
- await:await后面是可能会发生阻塞的方法,await关键字必须写在async函数里面
-
代码
import asyncio async def func(name): ''' await 可能会发生阻塞的方法 await关键字必须写在async函数里面 ''' print('start',name) await asyncio.sleep(1) print('end') loop=asyncio.get_event_loop() # loop.run_until_complete(func('xiaotong')) #调用一次 loop.run_until_complete(asyncio.wait([func('xiaotong'),func('minmin')])) #调用多次
-
协程的实现原理
-
生成器(Yield)中遇到yield的时候会直接切到函数外面,等到在此调用next的时候在执行下面的代码
-
代码
import time def sleep(n): print('start sleep') yield time.time()+n print('end sleep') def func(n): print('start') g=sleep(n) yield from g # yield from 相当于 await print('end') g1=func(1) g2=func(1.1) def run_until_complete(g1,g2): #run_until_complete就相当于run_until_complete ret1=next(g1) #睡完的时间 ret2=next(g2) #睡完的时间 ret_list={ret1:g1,ret2:g2} while ret_list: min_time=min(ret_list) #获取字典中睡眠最小的值 time.sleep(min_time-time.time()) try: next(ret_list[min_time]) #睡眠过后再次调用next except StopIteration:pass del ret_list[min_time] run_until_complete(g1,g2) #得:start start sleep start start sleep end sleep end end sleep end