閱讀補充:#
1. 基本概念:#
1.1 協程:#
- “協程 是為非搶佔式多任務產生子程序的計算機程序組件,協程允許不同入口點在不同位置暫停或開始執行程序”。
- 從技術的角度來說,“協程就是你可以暫停執行的函數”。
- 如果你把它理解成 “就像生成器一樣”,那麼你就想對了。
1.2 事件循環:#
- 事件循環 “是一種等待程序分配事件或消息的編程架構”。
- 基本上來說事件循環就是,“當 A 發生時,執行 B”。
- 或許最簡單的例子來解釋這一概念就是用每個瀏覽器中都存在的 JavaScript 事件循環。
- 當你點擊了某個東西(“當 A 發生時”),這一點擊動作會發送給 JavaScript 的事件循環,並檢查是否存在註冊過的 onclick 回調來處理這一點擊(“執行 B”)。
- 只要有註冊過的回調函數就會伴隨點擊動作的細節信息被執行。
- 事件循環被認為是一種循環是因為它不停地收集事件並通過循環來發如何應對這些事件。
1.3 Python 的事件循環:#
- 對 Python 來說,用來提供事件循環的 asyncio 被加入標準庫中。
- asyncio 重點解決網絡服務中的問題,事件循環在這裡將來自套接字(socket)的 I/O 已經準備好讀和 / 或寫作為 “當 A 發生時”(通過 selectors 模塊)。
- 除了 GUI 和 I/O,事件循環也經常用於在別的線程或子進程中執行代碼,並將事件循環作為調節機制(例如,合作式多任務)。
- 如果你恰好理解 Python 的 GIL,事件循環對於需要釋放 GIL 的地方很有用。
- 事件循環提供一種循環機制,讓你可以 “在 A 發生時,執行 B”。
- 基本上來說事件循環就是監聽當有什麼發生時,同時事件循環也關心這件事並執行相應的代碼。
- Python 3.4 以後通過標準庫 asyncio 獲得了事件循環的特性。
1.4 async, await:#
- 將 async/await 看作異步編程的 API
- 基本上 async 和 await 產生神奇的生成器,我們稱之為協程,
- 同時需要一些額外的支持例如 awaitable 對象以及將普通生成器轉化為協程。
- 所有這些加到一起支持並發,這樣才使得 Python 更好地支持異步編程。
- 相比類似功能的線程,這是一個更妙也更簡單的方法。
在 Python 3.4 中,用於異步編程並被標記為協程的函數看起來是這樣的:
# This also works in Python 3.5.
@asyncio.coroutine
def py34_coro():
yield from stuff()
Python 3.5 添加了 types.coroutine 修飾器,也可以像 asyncio.coroutine 一樣將生成器標記為協程。
你可以用 async def 來定義一個協程函數,雖然這個函數不能包含任何形式的 yield 語句;只有 return 和 await 可以從協程中返回值。
async def py35_coro():
await stuff()
你將發現不僅僅是 async,Python 3.5 還引入 await 表達式(只能用於 async def 中)。
雖然 await 的使用和 yield from 很像,但 await 可以接受的對象卻是不同的。
await 當然可以接受協程,因為協程的概念是所有這些的基礎。
但是當你使用 await 時,其接受的對象必須是 awaitable 對象:必須是定義了__await__() 方法且這一方法必須返回一個不是協程的迭代器。
協程本身也被認為是 awaitable 對象(這也是 collections.abc.Coroutine 繼承 collections.abc.Awaitable 的原因)。
這一定義遵循 Python 將大部分語法結構在底層轉化成方法調用的傳統,就像 a + b 實際上是 a.add(b) 或者 b.radd(a)。
為什麼基於 async 的協程和基於生成器的協程會在對應的暫停表達式上面有所不同?
主要原因是出於最優化 Python 性能的考量,確保你不會將剛好有同樣 API 的不同對象混為一談。
由於生成器默認實現協程的 API,因此很有可能在你希望用協程的時候錯用了一个生成器。
而由於並不是所有的生成器都可以用在基於協程的控制流中,你需要避免錯誤地使用生成器。
用 async def 可以定義得到協程。
定義協程的另一種方式是通過 types.coroutine 修飾器
-- 從技術實現的角度來說就是添加了 CO_ITERABLE_COROUTINE 標記
-- 或者是 collections.abc.Coroutine 的子類。
你只能通過基於生成器的定義來實現協程的暫停。
awaitable 對象要麼是一個協程要麼是一個定義了__await__() 方法的對象
-- 也就是 collections.abc.Awaitable
-- 且__await__() 必須返回一個不是協程的迭代器。
await 表達式基本上與 yield from 相同但只能接受 awaitable 對象(普通迭代器不行)。
async 定義的函數要麼包含 return 語句
-- 包括所有 Python 函數缺省的 return None
-- 和 / 或者 await 表達式(yield 表達式不行)。
async 函數的限制確保你不會將基於生成器的協程與普通的生成器混合使用,因為對這兩種生成器的期望是非常不同的。
1.5 關於 python 協程 和 golang 的對比討論:#
- From Python to Go and Back Again
- PPT
- 關於此 PPT 的觀點: go 比 pypy 性能高不了多少,但是複雜度和調試難度增加很高
- 結尾鼓吹 rust.
- 異步庫參考:
2. 源碼模塊:#
2.1 (futures.py)[./futures.py]#
2.1.1 參考:#
-
Python 協程:從 yield/send 到 async/await
- future 源碼剖析
-
concurrent.futures 源碼閱讀筆記(Python)
- concurrent.futures 是一個異步庫
- concurrent.futures — Asynchronous computation
2.1.2 生成器(Generator)VS 迭代器(iterator):#
-
improve-your-python-yield-and-generators-explained
- 譯文:提高你的 Python: 解釋‘yield’和‘Generators(生成器)’
- 在 Python 之外,最簡單的生成器應該是被稱為協程(coroutines)的東西。
- generator 是用來產生一系列值的
- yield 則像是 generator 函數的返回結果
- yield 唯一所做的另一件事就是保存一個 generator 函數的狀態
- generator 就是一個特殊類型的迭代器(iterator)
- 和迭代器相似,我們可以通過使用 next () 來從 generator 中獲取下一個值
- 通過隱式地調用 next () 來忽略一些值
-
- 生成器 (generator) 是一個特殊的迭代器,它的實現更簡單優雅。yield 是生成器實現 next() 方法的關鍵