第十七章-异步IO

异步IO的出现源自于CPU速度与IO速度彻底不匹配python

通常的能够采用多线程或者多进程的方式来解决IO等待的问题web

一样异步IO也能够解决同步IO所带来的问题服务器

常见的异步IO的实现方式是使用一个消息循环, 主线程不断的读取这个消息循环以便肯定IO操做是否完成多线程

1 协程

  协程(微线程, 纤程)并发

  通常子程序调用是一个入口一个出口, 调用的顺序也是明确的app

  可是协程不一样, 执行过程当中子程序内部中断, 就会转而执行别的子程序, 等待合适的时间返回 , 这样的行为相似于进程的切换 webapp

  正由于协程又相似于线程执行的特性, 可是子程序切换几乎没有切换开销, 所以性能很好异步

  并且协程还能够避免临界资源操做的问题async

  协程是一个线程执行函数

  构造协程通常是使用生成器, 利用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'))
相关文章
相关标签/搜索