我的笔记,不保证正确。html
虽说看到不少人不看好 asyncio,可是这个东西仍是必须学的。。 基于协程的异步,在不少语言中都有,学会了 Python 的,就一通百通。python
Python 的 asyncio 是经过 generator 实现的,要学习 async,先得复习下 generator.shell
众所周知,yield 是用于定义 generator 函数的关键字,调用该函数,会返回一个 generatorexpress
>>> def f(): ... yield 1 ... yield 2 ... >>> f() # 返回的是 generator <generator object f at 0x7f672c460570> >>> g = f() >>> next(g) # 经过 next 方法从 generator 获取值 1 >>> g.__next__() # next 方法实际是调用了 generator 的 __next__ 方法 2 >>> next(g) # 生成器运行结束,产生一个 StopIteration 的 exception Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
每次调用 next,generator 都只会运行到下一个 yield 关键字所在行,返回 yield 右侧的对象,而后暂停在该处,等待下一次 next 调用。编程
从上面的例子看,yield 就是延迟求值而已。**可是 yield 还有一个特性,就是它是一个 expression,有返回值!**看例子:并发
>>> def func(): ... r = yield 1 ... yield r ... >>> g = func() >>> next(g) 1 >>> next(g) # 经过 next 调用,yield 的返回值为 None >>> g2 = func() >>> next(g2) # 首先须要经过 next 调用,运行到 yield 语句处 1 >>> g2.send(419) # 如今用 send 方法,这会将当前所在的 yield 语句的值设置为你 send 的值,也就是 419 419 # 而后 generator 运行到下一个 yield,返回右边的值并暂停
generator 有四个实例函数:next、send 是刚刚已经介绍了的,此外还有 throw 用于从 yield 所在处抛出 Exception,和 close 用于关闭 Generator。详见 Generator-iterator methods异步
能够理解成是 yield <value> from <iterable>
,每次调用时它都会从 <iterable> 中取值,直到遇到 StopIteration。才会从下一个 yield 取值。async
>>> def f(): ... yield from [1, 2, 3, 4] # iterable ... yield 5 ... yield from range(4, 0, -1) # iterable ... >>> list(f()) [1, 2, 3, 4, 5, 4, 3, 2, 1]
固然,yield from <iterable>
也是一个 expression,也有值。它的值就是 StopIteration 异常的第一个参数,内置类型的这个值都是 None.ide
>>> def f(): ... r = yield from [1, 2] ... yield f"value of yield from is {r}" ... >>> list(f()) [1, 2, 'value of yield from is None']
当 <iterable> 是 generator 时,yield from
会直接将函数调用委托给这个子 generator,这里的调用包括了前面说过的 next、send、throw、close 四个函数。 并直接将 sub generator yield 的值 yield 给 caller.异步编程
generator 中的 return value
,语义上等同于 rasie StopIteration(value)
:
>>> def f(): ... yield 1 ... return 2 ... yield 3 # 永远不会被执行 ... >>> g = f() >>> next(g) 1 >>> next(g) # return 引起 StopIteration Traceback (most recent call last): File "<input>", line 1, in <module> StopIteration: 2 >>> next(g) # 再次调用,StopIteration 变成无参了。 Traceback (most recent call last): File "<input>", line 1, in <module> StopIteration
能够看到 return 引起了 StopIteration 异常,而 return 的值则成了该异常的第一个参数。
以前说过 yield from <sub generator>
表达式的值,就是该 <sub generator> 的 StopIteration 异常的第一个参数,所以:
>>> def f2(): ... a = yield from f() ... yield a # a 是 f() 中 return 的值 ... >>> list(f2()) [1, 2]
PEP 479 -- Change StopIteration handling inside generators 修改了StopIteration 的行为,该 PEP 令人为 raise 的 StopIteration 引起一个 RuntimeError。 该 PEP 在 Python 3.5 版本添加到 future 中,并在 Python 3.7 成为默认行为。 所以除非你确实想要引起异常,不然应该使用 return 来结束一个 generator 并返回值。
先了解一下 进程线程协程与并发并行 和 各类 IO 模型
asyncio 引入了两个新关键字:async 和 await,其中 async 能放在三个地方:
注意异步 generator、context manager,它的 protocol 都和同步的不一样,不能混为一谈。 具体而言,对同步 protocol xxx 函数,它的异步版本为 axxx,就是加个 a。
而 await,就至关于 yield from,差异在于 await 是异步的。还有咱们关心的是 await 表达式的值,而 yield from 中咱们更关心它向上层 yield 的值。
在 yield from 中,当前生成器调用另外一个生成器,当前生成器会挂起,直到另外一个生成器返回。
可是在 await 中,当前 Coroutine 挂起时, eventloop 会寻找其余 task 来跑,这就利用上了 IO 漫长的等待时间。
async for 是每次迭代都会 await 一次,若是迭代对象是 IO 操做,这个 IO 等待时间就会被利用上。
async with 也是一样,若是 context 的 enter 和 exit 是 IO 操做,这个 IO 时间就会被 eventloop 用于运行其余 task.
使用 asyncio 时,咱们要用 async def 将全部的 IO 操做都定义成异步操做。而后在调用时,都使用 await/async for/async with 来调用。
首先,每一个协程对象,都是一个独立的协程单元,协程对象之间能够异步运行。
协程须要放到 EventLoop 内运行,要运行一个协程 a,有三种方法:
concurrent.futures 是进程线程的异步执行,而 asyncio 是基于协程的单线程异步执行