1.什么是coroutine
coroutine,最早我是在lua里面看到的,coroutine最大的好处是可以保存堆栈,让程序得以继续执行,在python里面,一般是利用yield来实现,具体可以看如下文章:
http://www.cnblogs.com/tqsummer/archive/2010/12/27/1917927.html
python中的yield以及yield from语法可以让程序支持coroutine
2.asyncio库
Python3中,提供了基于coroutine的异步IO库,就是asyncio
https://docs.python.org/3/library/asyncio.html
2.1 event loop
asyncio库一个重要的概念就是事件循环,只有启动事件循环以后,才可以让coroutine任务得以继续执行,如果event loop停止或者暂停,那么整个异步io也停止或者暂停,类似于操作系统的事件循环机制
asyncio的内部是基于selector(也可能是epoll)或者windows iocp来实现的,这也是为什么需要启动一个event loop,event loop可以看成是对各个平台的异步IO"等待"这个操作的封装
2.2 asyncio库
cotoutine
coroutine即为一个支持写成的函数,可以利用iscoroutinefunction来判断是否coroutine函数,这个函数需要@asyncio.coroutine来修饰
利用@asyncio.coroutine修饰以后,这个函数可以支持await(python 3.5) 或者 yield from语法,一旦执行yield from 语法以后,asyncio将会挂起当前的coroutine,去执行其他的coroutine
如下代码:
当开始运行event loop以后
1.开始执行sleep3s
2.当程序开始进入睡眠以后,event loop不会停止当前线程,而是挂起当前函数,执行下一个coroutine,即sleep5s
3.sleep5s开始进入睡眠,挂起当前的函数
4.event loop检测到sleep 3s时间已经到了,于是重新执行被挂起的sleep3s,sleep3s执行完毕
5.sleep5s时间已经到了,于是重新执行被挂起的sleep5s,sleep5s执行完毕
基于coroutine的服务器
如上图,_tcp_listen创建了一个tcp 服务器,并且收到一个tcp链接以后,创建一个_tcp_recv的任务,然后开始监听tcp连接的数据
与基于回调的库区别
coroutine版本代码在recv上逻辑处理代码是线性的而不是断裂的,基于回调的代码则经常需要乱跳,可读性较高
而且不用创建临时的数据去保存一些变量,上图中的self.tcp_clients就是因为老版本是基于回调的,需要这个数据成员去存储(懒得再自己重新写代码,这分是重构某个工具到一般时候的代码)
扩展asyncio
因为项目里面需要知道对方udp的地址,因此需要扩展sock_recv,利用socket本身提供的recv_from
看了下loop.socket_recv的源码,loop.socket_recv的
在调用之前 必须将socket设置为非阻塞,否则会报错
关键点在于add_reader这个函数将socket传送给异步IO(默认是selector),并且传送一个回调进去(在这个例子里面是_sock_recv自己这个函数,只要有数据可以读取以后,将会再次调用_sock_recv这个函数
asyncio也不止支持sleep以及io操作,还支持多进程,lock等操作
只要将上图的data = sock.recv(n)改为data, addr = sock.recv_from(n)就可以了
在3.5中,新增加了async以及await的语法,用于代替asynio.coroutine以及yield from
Python3.5 的async以及await