Python异步编程协程使用_事件循环解析【教程】

21次阅读

python异步编程的核心是协程与事件循环协同工作:协程需由事件循环调度执行,async def定义协程函数,调用返回协程对象,须通过asyncio.run()、create_task()或await触发运行;事件循环负责I/O监听、协程调度与任务管理;await表示让出控制权,仅适用于awaitable对象;常见错误包括忘记await、混用同步阻塞调用、误用time.sleep等。

Python异步编程协程使用_事件循环解析【教程】

Python异步编程的核心是协程(coroutine)与事件循环(Event loop)的配合。协程本身不会自动运行,必须由事件循环调度执行;理解二者关系,是写出可靠异步代码的前提。

协程函数 ≠ 协程对象 ≠ 正在运行的任务

定义一个 async def 函数,只是创建了一个协程函数,调用它返回的是一个协程对象(coroutine Object),不是立即执行,也不会启动异步行为。

例如:

async def fetch_data():
await asyncio.sleep(1)
return "done"

立即学习Python免费学习笔记(深入)”;

执行 fetch_data() 得到的是一个协程对象,打印出来类似 。此时什么都没发生。

要让它跑起来,得交给事件循环:

  • asyncio.run(fetch_data()) —— 自动创建新事件循环并运行,适合脚本入口
  • loop.create_task(coro)asyncio.create_task(coro) —— 把协程包装成任务(Task),加入当前事件循环待调度
  • await coro —— 在另一个协程内部等待它,此时调用者协程会挂起,直到被等待的协程完成

事件循环是异步程序的“调度中心”

事件循环负责:监听 I/O 就绪、调度协程恢复、管理任务生命周期、处理回调和定时器。它不并发执行 Python 字节码,而是通过挂起/恢复协程实现单线程内的协作式并发。

关键事实:

  • 一个线程最多运行一个事件循环(主线程默认无循环,需显式启动)
  • asyncio.run() 每次都新建并关闭循环,不能在已有循环中重复调用
  • jupyter 或某些框架(如 fastapi)中,循环可能已由环境启动,直接调用 asyncio.run() 会报错 “event loop is running”
  • 获取当前循环:用 asyncio.get_running_loop()(推荐),而非过时的 get_event_loop()

await 的本质是“让出控制权”

await 表达式只能出现在协程函数中,它的作用不是“等待时间过去”,而是告诉事件循环:“我现在要等某个东西(比如网络响应、文件读取、另一个协程),请先去干别的,等它就绪了再回来叫我。”

能被 await 的对象必须是 awaitable,包括:

  • 协程对象(async def 返回值)
  • 实现了 __await__ 方法的对象(如 asyncio.Futureasyncio.Task
  • 使用 types.coroutine 装饰的生成器函数(较少见)

普通函数、列表、字符串等不可 await,否则抛 TypeError: object xxx can't be used in 'await' expression

常见误区与调试提示

新手容易卡在这几个地方:

  • 忘记 await:调用协程函数却不 await,只得到协程对象,后续逻辑不会执行(也没有报错)
  • 混用同步阻塞调用:在协程里写 time.sleep(2)requests.get(),会阻塞整个事件循环,失去异步意义
  • 误以为 asyncio.sleep = 真实休眠:它只是向事件循环注册一个“1秒后唤醒我”的计划,期间循环可调度其他任务
  • 多任务没并发执行:用 await task1(); await task2() 是串行;要用 await asyncio.gather(task1(), task2())asyncio.create_task() 并发启动

调试时可加 print(f"running at {time.time():.2f}") 观察实际执行顺序,比想象中更贴近“多任务交替推进”而非“同时运行”。

text=ZqhQzanResources