asyncio
- 回调模式编码的复杂度高,
- 同步编码的并发性不高,
- 多线程编程需要线程间同步, 通过lock机制,会影响效率,
所以我们就需要采用同步的方式去编写异步的代码,
1 | def get_url(url): |
这样我们也就不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
所以就有了协程
协程,又称微线程,也就是一个可以暂停的函数,等有了结果再切换回去,是我们程序员自己来切换的
那有什么办法能暂停函数,然后再回来呢?
我们想到了生成器,所以我们这里再提一下 生成器
生成器
send
1 | def get_url(): |
yield from
:允许发送生成器
1 | def gen1(): |
案例:
1 | # 我们这样一个数据,我们希望获取一个总数,然后跟一个原来的列表 |
asyncio
异步模块
asyncio
是python 用于解决异步IO变成的一整套方案
实现了:
- 包括各种特定系统实现的模块化的事件循环
- 传输和协议抽象
- 对
TCP,UDP,SSL
,子进程,延时调用,以及其他的具体支持 - 模块futures模块,但适用于事件循环使用的Future类
- 基于
yield from
的协议和任务.可以让你用顺序的方式编写并发代码 - 必须使用一个将产生阻塞IO的调用时,有接口可以把这个事件转移到线程池
- 模仿
threading
模块中的同步原语,可以用在单线程内的协程之间
三个要素:事件循环,回调函数(驱动生成器),以及IO多路复用
简单使用
1 | import asyncio |
这样看我们并不能感受到异步带来的好处,我们接下来创建100个任务来模拟
1 | import asyncio |
取消task
1 | async def cancel_test(sleep_time): |
子协程调度
案例:
1 | import asyncio |
该图在3.6的官方文档中
解析:
线程池结合asyncio
线程池为什么要跟asyncio 结合呢?
由于协程中不能处理阻塞IO 如mysql库是阻塞的,如果我们需要在协程中强行使用,怎么办?asyncio也可以使用多线程下面我们来模拟一下
1 | import asyncio |
其实就是将阻塞的函数放到线程池中
建议大家不要在协程中调用阻塞方式,如果非要运行的话,建议使用线程池
future 和 task
future
是一个结果容器,用来存放结果
task
是future的子类,主要用作协程与future的桥梁
1 | def __init__(self, coro, *, loop=None): |
__step
方法
1 | def __step(self, exc=None): |
协程还是需要很多底层知识的,还是乖乖去看一下 底层原理