8.16. 小结

你已经走完了 asyncio 的 API——脚本在单个 CPU 上并发运行多个任务所需的各个部件:

  • 协作式调度——本模块其余内容所依赖的模型。正在运行的协程独占事件循环,直到它 await;切换只发生在那些 await 处。

  • 协程与任务——async def 定义一个工作单元;asyncio.create_task() 并发地调度一个工作单元,并返回一个 Task,应用程序之后可以对它进行等待、取消或识别。

  • 事件循环——运行协程和任务的引擎。asyncio.run() 是大多数脚本所需的唯一入口点;Loop 类为需要它的少数情况暴露了其余功能。

  • 协调——gather() 用于扇出和扇入,wait_for() 用于截止时间,Task.cancel 以及 finally 子句的清理模式,异常在任务和 gather 调用中的传播,还有事件循环的异常处理程序钩子。

  • 同步原语——Event 用于协程之间的信号传递,Lock 用于跨 await 串行化对共享资源的访问,ThreadSafeFlag 用于从中断处理程序唤醒一个 asyncio 任务。

  • 自定义异步对象——让应用程序类能够接入 asyncio 惯用法的语言钩子。__await__ 用于那些本身就是 await 目标的对象,__aiter__ / __anext__ 用于 async for__aenter__ / __aexit__ 用于 async with

  • 帧捕获——把 csi.CSI.snapshot() 转换为对 await 友好的协程的包装器,使得一个捕获循环可以与其他 asyncio 工作并行运行。

  • 陷阱——遗忘的 await、没有让出的紧密循环、被吞掉的取消、跨 await 被改动的共享状态,以及其余 asyncio 特有的陷阱。

这已经足以编写在同一个事件循环上混合摄像头工作、硬件 I/O 和并发后台工作的程序了。

8.16.1. 稍后使用本参考

把 asyncio 各章当作参考资料;回来查阅 async with 的写法或 gather() 在某个同级任务失败时的确切行为,正是它们的预期用途。当问题只是“这个调用的确切名称是什么”时,asyncio 参考页在一处列出了每个函数和类。

对于构建在本模块之上的更丰富的原语——信号量、队列、屏障,以及大量面向应用的辅助工具——peterhinch/micropython-async 仓库是社区维护的标准来源。

8.16.2. 下一步去往何处

网络是下一个重要主题。asyncio.open_connection()asyncio.start_server() 以及 Stream 类是 asyncio 脚本从协程内部与网络其余部分通信的方式,再加上底层的 networksocket 模块。你学到的关于 awaitTask、取消以及同步原语的一切都可以直接沿用。