异步IO的出现源自于CPU速度与IO速度完全不匹配
一般的可以采用多线程或者多进程的方式来解决IO等待的问题
同样异步IO也可以解决同步IO所带来的问题
常见的异步IO的实现方式是使用一个消息循环, 主线程不断的读取这个消息循环以便确定IO操作是否完成
1 协程
协程(微线程, 纤程)
一般子程序调用是一个入口一个出口, 调用的顺序也是明确的
但是协程不同, 执行过程中子程序内部中断, 就会转而执行别的子程序, 等待合适的时间返回 , 这样的行为类似于进程的切换
正因为协程又类似于线程执行的特性, 但是子程序切换几乎没有切换开销, 因此性能很好
而且协程还可以避免临界资源操作的问题
协程是一个线程执行
构造协程一般是使用生成器, 利用send()发送相互传递数据
常见的生产者消费者的范例
def consumer(): r = ‘‘ while True: n = yield r if not n: return print(‘[CONSUMER] Consuming %s...‘ % n) r = ‘200 OK‘ def produce(c): c.send(None) n = 0 while n < 5: n = n + 1 print(‘[PRODUCER] Producing %s...‘ % n) r = c.send(n) print(‘[PRODUCER] Consumer return: %s‘ % r) c.close() c = consumer() produce(c)
2 asyncio
基本使用为
import asyncio @asyncio.coroutine def hello(): print("Hello world!") # 异步调用asyncio.sleep(1): r = yield from asyncio.sleep(1) print("Hello again!") # 获取EventLoop: loop = asyncio.get_event_loop() # 执行coroutine loop.run_until_complete(hello()) loop.close()
使用装饰器asyncio.coroutine装饰一个执行函数, 这样便可以生成一个异步执行IO的函数
使用asyncio.get_event_loop()创建一个循环
通过这个循环的对象执行run_until_complete()来执行异步IO
其中run_until_complete()可以传入一个函数执行
如果需要多个函数的话, 就需要将这些函数执行方法一个list中, 并使用asyncid.wait(list)
tasks = [hello(), hello()] loop.run_until_complete(asyncio.wait(tasks))
最后调用close()结束异步IO
其中函数的写法是 需要使用 yield from来实现异步IO
获取网页的函数编写如下
@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() if line == b‘\r\n‘: break print(‘%s header > %s‘ % (host, line.decode(‘utf-8‘).rstrip())) writer.close()
3 async和await
使用asyncio.coroutine可以吧一个生成器标记为coroutine类型
然后在内部使用yield from调用另一个coroutine实现异步操作
为了进一步简化操作, 出现了async和await, 这是在python3.5开始支持的语法
简化如下
1) 装饰器asyncio.corontine替换为async
2) yield from替换为await
因此上述代码可以简化为
async def hello(): print("Hello world!") r = await asyncio.sleep(1) print("Hello again!") async def wget(host): print(‘wget %s...‘ % host) connect = asyncio.open_connection(host, 80) reader, writer = await connect header = ‘GET / HTTP/1.0\r\nHost: %s\r\n\r\n‘ % host writer.write(header.encode(‘utf-8‘)) await writer.drain() while True: line = await reader.readline() if line == b‘\r\n‘: break print(‘%s header > %s‘ % (host, line.decode(‘utf-8‘).rstrip())) writer.close()
4 aiohttp
asyncio实现了单线程并发io操作, 如果只是应用于客户端, 那么作用还不是那么明显
针对于服务器, asyncio可能发挥更好的效果, 基于http协议实现的asyncio就是aiohttp
安装
pip install aiohttp
1) 导入包
import asyncio from aiohttp import web
2) 实现mian代码
if __name__ == ‘__main__‘: loop = asyncio.get_event_loop() loop.run_until_complete(init(loop)) loop.run_forever()
3) 编写初始化函数
将循环loop作为参数传入, 用于创建webapp
添加路由, 并指定处理函数
最后创建服务器, 利用create_server()创建TCP服务
async def init(loop): app = web.Application(loop=loop) app.router.add_route(‘GET‘, ‘/‘, index) app.router.add_route(‘GET‘, ‘/hello/{name}‘, hello) srv = await loop.create_server(app.make_handler(), ‘127.0.0.1‘, 8000) print(‘Server started at http://127.0.0.1:8000...‘) return srv
4) 编写两个路由的处理函数
async def index(request): await asyncio.sleep(0.5) return web.Response(body=b‘<h1>Index</h1>‘) async def hello(request): await asyncio.sleep(0.5) text = ‘<h1>hello, %s!</h1>‘ % request.match_info[‘name‘] return web.Response(body=text.encode(‘utf-8‘))