8.3. 事件循环¶
事件循环是 asyncio 在底层运行的引擎。它维护着程序中每个任务的列表,让每个任务运行到它的下一个 await,然后转到下一个准备就绪的任务。当没有准备就绪的任务时,它会等待——这种实际的等待正是让 CPU 可供固件运行其他事务、并让省电休眠得以生效的原因——直到某个任务所等待的东西变得可用,然后恢复该任务。如此往复,永不停歇。
大多数应用程序从不直接与循环交互。循环是调用 asyncio.run() 的结果;应用程序编写协程,将它们作为任务调度,其余的工作则交由循环完成。
8.3.1. asyncio.run() 实际做了什么¶
单单一次调用::
asyncio.run(main())
是循环代表应用程序所管理的一个更长序列的简写:
如果事件循环尚不存在,则创建它。
将提供的协程包裹在一个任务中,并将其调度为循环的顶层入口点。
运行循环——逐步执行准备就绪的任务,没有就绪任务时等待,当任务的 await 完成时恢复任务——直到顶层任务返回或抛出异常。
取消应用程序所创建的、仍在运行的任何任务。
返回顶层协程返回的任何值(或重新抛出它所抛出的任何异常)。
8.3.2. 每个程序一个循环¶
MicroPython 的 asyncio 只有一个事件循环,仅此而已。无法创建一个全新的循环,也无法在一个循环内部嵌套另一个循环。从一个已经在循环上运行的协程内部调用 asyncio.run() 是一个错误;循环已经在那里了,该协程只需要 await 它想要启动的任何东西。
实际上,这条规则与上一页的结尾那句话是一样的:每个程序恰好有一次 asyncio.run() 调用,位于顶层,其后跟着单独一个 async def main()。其他一切都存在于 main 内部。
8.3.3. 直接访问循环¶
对于应用程序确实需要触及循环本身的少数情况——主要是诊断和异常处理程序——asyncio.get_event_loop() 返回 Loop 对象。应用程序可以从这里安装自定义异常处理程序、检查循环正在做什么,或者(极少数情况下)直接调用 create_task() 而不是 asyncio.create_task()(它们是同一个操作)。
Loop 所公开的全部方法——run_forever()、stop()、set_exception_handler() 等等——会在本节后面的 循环控制 页面中介绍。在那之前,asyncio.run(main()) 就是应用程序所需的全部。