8.12. Trình quản lý ngữ cảnh bất đồng bộ

Tổng quan Python đã giới thiệu trình quản lý ngữ cảnh -- các đối tượng mà khối with điều khiển, với __enter__ chạy khi vào và __exit__ chạy khi ra dù khối kết thúc như thế nào. Trình quản lý ngữ cảnh bất đồng bộ là cùng ý tưởng được chuyển vào asyncio: __aenter____aexit__coroutine, nên phần thiết lập hoặc dọn dẹp có thể await một điều gì đó. Khối điều khiển chúng là async with.

Chúng ta đã dùng một cái. asyncio.Lock là một trình quản lý ngữ cảnh bất đồng bộ; trang locks đã sử dụng nó như async with bus_lock: .... Trang này nói về việc viết một cái cho tài nguyên của ứng dụng.

8.12.1. Khi nào nên viết

Khi ứng dụng có một tài nguyên cần thiết lập và giải phóng theo cặp, và ít nhất một trong hai phía phải await điều gì đó. Kết nối mạng, các cảm biến cần độ trễ ổn định sau khi cấu hình, bất cứ thứ gì khóa khi vào và mở khóa khi ra. Dạng with đồng bộ thông thường không áp dụng vì __enter____exit__ của nó không thể là coroutine.

8.12.2. Hai phương thức

  • async def __aenter__(self) chạy khi vào khối. Bất cứ thứ gì nó trả về sẽ là giá trị mà mệnh đề as name tùy chọn của async with gán vào. Trả về self là hình thức phổ biến nhất, nhưng bất kỳ giá trị nào cũng được.

  • async def __aexit__(self, exc_type, exc, tb) chạy khi thoát khối. exc_typeNone khi thoát bình thường; khi có ngoại lệ (hoặc bị hủy) nó là lớp của ngoại lệ đó, và exc là thực thể. Trả về giá trị truthy báo Python rằng ngoại lệ đã được xử lý và không nên lan truyền. Trả về None (trường hợp thông thường) cho phép ngoại lệ tiếp tục đi lên chuỗi gọi sau khi mã dọn dẹp chạy.

8.12.3. Ví dụ minh họa

Một bộ bọc đèn spotlight bật LED cho thân khối, với một độ trễ ổn định ngắn để ánh sáng ổn định trước khi chụp ảnh, và tắt LED khi thoát ra:

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())

Khi vào khối, __aenter__ chạy: LED bật, await chờ 50 ms nhường cho vòng lặp để các coroutine khác có thể tiến lên trong lúc đó, và thân khối bắt đầu sau khi quá trình chờ hoàn tất. Khi khối kết thúc -- thoát bình thường, có ngoại lệ, hay bị hủy -- __aexit__ chạy và LED tắt trở lại. Mã dọn dẹp chạy trong mọi trường hợp; đó là bảo đảm mà async with cung cấp.

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.