python3.6 版本以后,asyncio 与 aiohttp 异步模块的使用说明
asyncio
概述
在Python3.6后,可以通过关键词async def来定义一个coroutine协程,协程就相当于未来需要完成的任务,多个协程就是多个需要完成的任务,多个协程可以进一步封装到一个task对象中,task就是一个储存任务的盒子。此时,装在盒子里的任务并没有真正的运行,需要把它接入到一个监视器中使它运行,同时监视器还要持续不断的盯着盒子里的任务运行到了哪一步,这个持续不断的监视器就用一个循环对象loop来实现。 原话链接 单来说在一个线程里,先后执行 AB 两个任务,但是当A遇到耗时操作(网络等待、文件读写等),这个时候 gevent 会让 A 继续执行,但是同时也会开始执行B任务,如果B在遇到耗时操作同时A又执行完了耗时操作
基础示例
1 | import asyncio |
代码注释
- 启动入口 asyncio.run()
- 并发运行asyncio任务 asyncio.create_task()
- 并发运行asyncio任务 asyncio.gather()
- 等待对象 await
- 休眠 asyncio.sleep() 挂起当前任务,允许允许其他任务
核心解析
-
event_loop
事件循环 理解为一个循环的池,里面存放一些async关键词定义的协程函数,只有放到循环池里才能执行。 -
coroutine
协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。 -
task
任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。 -
future
:代表将来执行或没有执行的任务的结果。它和task上没有本质的区别。 -
async/await
关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
简单使用1
2
3
4
5
6
7
8import asyncio
async def do_some_work(x): # 使用async关键字定义协程
print('Waiting: ', x)
coroutine = do_some_work(2) # 创建协程对象
loop = asyncio.get_event_loop() # 创建一个事件循环(池)
loop.run_until_complete(coroutine) # 将协程对象包装并注册协程对象
创建 task
协程对象不能直接运行,需要包装成任务才能运行,上面是通过run_until_complete()方法包装成task(隐式包装),还有下面两种方式进行显式包装:
-
task = asyncio.ensure_future(coroutine)
-
task = loop.create_task(coroutine)
1 | import time, asyncio |
创建 task 后
- task 在加入事件循环前十 pending 状态
- 加入 loop 后运行中是 running 状态
- loop 调用完是 Done 状态
- 运行完是 finished 状态
task 显性包装相比隐性包装有了协程函数的状态
loop.run_until_complete() 接收 future 参数,指协程函数 task是future的子类
绑定回调函数
通过 task 的 task.add_done_callback(callback) 绑定回调函数,接收一个 future 对象参数如 task,在内部通过 future.result() 获取其返回值
1 | import asyncio |
await 挂起耗时操作
task 对象是顺序执行的, 因为在异步中没有声明哪些是耗时操作,所以会顺序执行,await作用就是提示哪些是耗时操作,可以对耗时操作进行挂起。
1 | import asyncio |
传统执行的情况下,会阻塞顺序执行。 开启await挂起耗时操作的情况下会异步执行
aiohttp 网络访问
pip3 install aiohttp
使用案例,连续访问100次百度只需要一秒不到
1 | import aiohttp, asyncio, time |
并发访问
- loop.run_until_complete(syncio.wait(tasks)) 来实现协程并发,传入 task列表
- loop.run_until_complete(asyncio.gather(*tasks)) asyncio.gather 会将列表中不是 task 的 coro 预先封装为 future, 而 wait 则不会。
两种方法效果相同,但是 wait 核 gather 返回值不同
多进程配合
如果这也满足不了你,你可以开启多进程配合使用,asyncio、aiohttp需要配合aiomultiprocess库使用,版本要求至少3.6
1 | import asyncio |
关闭协程
- 关闭单个 task
- 关闭 loop
具体涉及函数
- asyncio.Task.all_tasks() 获取事件循环任务列表
- KeyboardInterrupt 捕获停止异常(Ctrl+C)
- loop.stop() 停止任务循环
- task.cancel() 取消单个任务
- loop.run_forever()
- loop.close() 关闭事件循环,不然会重启
同类型 gevent 模块
python程序实现的一种单线程下的多任务执行调度器,简单来说在一个线程里,先后执行 AB 两个任务,但是当A遇到耗时操作(网络等待、文件读写等),这个时候 gevent 会让 A 继续执行,但是同时也会开始执行B任务,如果B在遇到耗时操作同时A又执行完了耗时操作,gevent 又继续执行 A。 这里是我gevent使用Demo