8.12. 非同步情境管理器¶
Python 概觀 介紹了情境管理器 -- 由 with 區塊驅動的物件,__enter__ 在進入時執行,而 __exit__ 則在退出時執行,無論區塊如何結束。非同步情境管理器是同樣的概念移植到 asyncio 中:__aenter__ 和 __aexit__ 是協程,因此設定或清理工作都可以 await 某些項目。驅動它們的區塊是 async with。
我們已經用過一個了。asyncio.Lock 就是一個非同步情境管理器;鎖 頁面以 async with bus_lock: ... 的形式使用了它。本頁則是關於為應用程式自己的資源撰寫一個非同步情境管理器。
8.12.1. 何時該撰寫一個¶
當應用程式有一項資源需要成對的設定與拆解,且這兩端之中至少有一端必須 await 某些項目時。例如網路連線、設定後需要一段安定延遲的感測器、任何在進入時鎖定某物並在退出時解鎖的事物。單純的同步 with 形式並不適用,因為它的 __enter__ 和 __exit__ 不能是協程。
8.12.2. 兩個方法¶
8.12.3. 一個實作範例¶
一個聚光燈包裝器,在區塊主體執行期間點亮一個 LED,並加上一段短暫的安定延遲,使照明在任何拍攝發生之前都已穩定,然後在退出途中再次熄滅該 LED:
import asyncio
from machine import LED
class Spotlight:
def __init__(self, led):
self._led = led
async def __aenter__(self):
self._led.on()
await asyncio.sleep_ms(50)
return self
async def __aexit__(self, exc_type, exc, tb):
self._led.off()
async def main():
led = LED("LED_WHITE")
async with Spotlight(led):
# work that benefits from steady illumination
await asyncio.sleep_ms(200)
asyncio.run(main())
進入區塊時,__aenter__ 會執行:LED 點亮,那 50 ms 的安定 await 讓出控制權給迴圈,使其他協程在此期間能夠推進,而區塊主體則在等待完成後開始執行。當區塊結束時 -- 無論是正常退出、發生例外、還是被取消 -- __aexit__ 都會執行而 LED 會重新熄滅。清理工作在每種情況下都會執行;這就是 async with 所提供的保證。
The frame capture page
shows how to make csi.CSI.snapshot() await-friendly; once that wrapper is in hand, the body of an
async with Spotlight(led): block would typically be a
capture loop running under the steady illumination.