- 异步IO
异步IO是指当代码执行到一个耗时IO操作时,它只发出IO指令,然后就去执行其他的代码,并不等待IO的执行结果。当一段时间后,IO操作返回结果时再通知CPU进行处理。
异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”的过程:
loop = get_event_loop()
while True:
event = loop.get_event()
process_event(event)
- asyncio
asyncio是python3.4版本引入的标准库,直接内置了对异步io的支持,它的编程模型就是一个消息循环。从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到Eventloop中执行,就实现了异步IO。
实例:
用asyncio的一部网络来获取sina、sohu和163的网站首页:
import asyncio
@asyncio.coroutine
def wget(host):
print('wget %s...' % host)
connect = asyncio.open_connection(host, 80)
reader, writer = yield from connect
header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8'))
yield from writer.drain()
while True:
line = yield from reader.readline()
#yield from asyncio.sleep(2)
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
# Ignore the body, close the socket
writer.close()
loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
执行结果如下:
wget www.sina.com.cn...
wget www.163.com...
wget www.sohu.com...
www.sina.com.cn
www.sina.com.cn header > HTTP/1.1 200 OK
www.sina.com.cn header > Server: nginx
www.sina.com.cn header > Date: Mon, 19 Sep 2016 03:03:30 GMT
www.sina.com.cn header > Content-Type: text/html
www.sina.com.cn header > Last-Modified: Mon, 19 Sep 2016 03:03:13 GMT
www.sina.com.cn header > Vary: Accept-Encoding
www.sina.com.cn header > Expires: Mon, 19 Sep 2016 03:04:30 GMT
www.sina.com.cn header > Cache-Control: max-age=60
www.sina.com.cn header > X-Powered-By: schi_v1.02
www.sina.com.cn header > Age: 52
www.sina.com.cn header > Content-Length: 593409
www.sina.com.cn header > X-Cache: HIT from localhost
www.sina.com.cn header > Connection: close
www.163.com
www.163.com header > HTTP/1.1 200 OK
www.163.com header > Expires: Mon, 19 Sep 2016 03:05:44 GMT
www.163.com header > Date: Mon, 19 Sep 2016 03:04:24 GMT
www.163.com header > Server: nginx
www.163.com header > Content-Type: text/html; charset=GBK
www.163.com header > Vary: Accept-Encoding,User-Agent,Accept
www.163.com header > Cache-Control: max-age=80
www.163.com header > X-Via: 1.1 czdx87:4 (Cdn Cache Server V2.0), 1.1 jifang134:10 (Cdn Cache Server V2.0)
www.163.com header > Connection: close
www.sohu.com
www.sohu.com header > HTTP/1.1 200 OK
www.sohu.com header > Content-Type: text/html
www.sohu.com header > Content-Length: 92145
www.sohu.com header > Connection: close
www.sohu.com header > Date: Mon, 19 Sep 2016 03:02:52 GMT
www.sohu.com header > Server: SWS
www.sohu.com header > Vary: Accept-Encoding
www.sohu.com header > Cache-Control: no-transform, max-age=120
www.sohu.com header > Expires: Mon, 19 Sep 2016 03:04:52 GMT
www.sohu.com header > Last-Modified: Mon, 19 Sep 2016 03:02:45 GMT
www.sohu.com header > Content-Encoding: gzip
www.sohu.com header > X-RS: 17799606.26974656.25737794
www.sohu.com header > FSS-Cache: HIT from 5397379.8739725.6791606
www.sohu.com header > FSS-Proxy: Powered by 3496806.4938608.4891004
在上述执行结果中,按照正常的异步IO的逻辑,打印出header的host不应该连续,但实际上,出现这种结果的原因是因为line = yield from reader.readline() 的执行时间非常短,还没有等到线程去执行其他任务的时候就已经执行完毕,所以就出现了连续打印的结果。(个人理解)
比如将上述注释掉的yield from asyncio.sleep(2)还原回来,则执行结果就变成了如下结果:
wget www.sina.com.cn...
wget www.163.com...
wget www.sohu.com...
www.sina.com.cn
www.163.com
www.sohu.com
www.sina.com.cn header > HTTP/1.1 200 OK
www.sohu.com header > HTTP/1.1 200 OK
www.sina.com.cn header > Server: nginx
www.sohu.com header > Content-Type: text/html
www.163.com header > HTTP/1.1 200 OK
www.sina.com.cn header > Date: Mon, 19 Sep 2016 03:07:54 GMT
www.sohu.com header > Content-Length: 92072
www.163.com header > Date: Mon, 19 Sep 2016 03:08:56 GMT
www.sina.com.cn header > Content-Type: text/html
www.sohu.com header > Connection: close
www.163.com header > Server: openresty
www.sina.com.cn header > Last-Modified: Mon, 19 Sep 2016 03:06:45 GMT
www.sohu.com header > Date: Mon, 19 Sep 2016 03:05:22 GMT
www.163.com header > Content-Type: text/html; charset=GBK
www.sina.com.cn header > Vary: Accept-Encoding
拓展
yield
yield的功能类似于return,加上yield的函数便不再是普通的函数,而变成了生成器。每当代码执行到了yield,代码就会中断执行,下次再继续从中断处继续执行。具体参考如下:
yield的作用yield和yield from
yield from会把内嵌的generator输出当做当前generator输出的一部分。
def g(x):
... yield from range(x, 0, -1)
... yield from range(x)
...
list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]
也就是说yield from是可以将generator串联的。