我的筆記,不保證正確。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 是基於協程的單線程異步執行