人人都会AI编程

asyncio 核心原理:事件循环、协程对象、任务

更新时间:2026-07-12

asyncio 是 Python 标准库提供的异步 I/O 框架,它的核心思想是在单线程内通过协作式多任务实现高并发。要理解它怎么工作,关键要搞懂三个核心概念:事件循环、协程对象、任务。

事件循环(Event Loop)

  • 是什么:事件循环就是一个“一直跑的程序大管家”,它维护一个任务队列,不断取出任务执行,遇到需要等待的操作(如网络请求、文件读取)就挂起当前任务,切换到其他可执行的任务,等 I/O 完成后再把挂起的任务放回队列继续执行。
  • 怎么理解:可以把它想象成一个单线程的服务员,给多桌客人点菜。他不会站在一桌等着厨房炒菜,而是点完菜立刻去服务下一桌,厨房叫号时再端菜给对应的客人。这样一个人也能同时服务多桌。
  • 在代码中如何获得asyncio.run(main()) 会创建一个事件循环,运行你写的 main 协程。在 main 内部,await 语句就是“我这边要等一会儿,你先去干别的”。

协程对象(Coroutine Object)

  • 是什么:用 async def 定义的函数调用后返回的不是普通函数的返回值,而是一个协程对象。协程对象代表一段可暂停、可恢复的执行流程。
  • 关键规则
  • 调用 async def 函数并不会执行函数体,而是返回一个协程对象。
  • 要让协程真正执行,必须通过 await 或将其包装成任务丢给事件循环。
  • await 只能用在 async def 函数内部,后面可以跟一个协程对象或任何可等待对象(如 Task、Future)。
  • 简单示例
  async def say_hello():
      print("Hello")
      await asyncio.sleep(1)   # 模拟耗时 I/O,让出控制权
      print("World")

  # 下面直接调用只返回协程对象,不会打印任何内容
  coro = say_hello()          # <coroutine object say_hello at 0x...>

  # 要真正执行,必须用 asyncio.run()
  asyncio.run(say_hello())
  

任务(Task)

  • 是什么:Task 是对协程对象的一种高级封装,它能让一个协程对象在事件循环中“后台”并发执行。你可以把它当成启动了一个异步的“轻量级线程”。
  • 为什么需要 Task:单纯 await coro 会当前协程会阻塞等待另一个协程执行完才继续。如果想让多个协程同时跑(比如同时下载三个网页),就需要把它们包装成 Task,让事件循环调度它们交替执行。
  • 创建 Task 的方法
  • asyncio.create_task(coro):最常用,把协程对象注册进当前运行的事件循环,立即返回一个 Task 对象。
  • asyncio.ensure_future(coro):老式写法,功能类似。
  • 并发例子
  async def download(url):
      # 模拟下载耗时
      await asyncio.sleep(1)
      print(f"下载完成: {url}")

  async def main():
      # 将两个协程包装成 Task,让它们并发运行
      task1 = asyncio.create_task(download("url1"))
      task2 = asyncio.create_task(download("url2"))

      # 等待所有任务完成
      await task1
      await task2

  asyncio.run(main())
  # 输出:大约 1 秒后同时打印两行下载完成,而非先等 1 秒再等 1 秒
  

三者关系总结

  • 事件循环 是调度中心,始终在运行。
  • 协程对象 是可暂停的函数执行体,需要通过某些方式激活。
  • Task 是协程对象的“并发包装”,让它们能在事件循环中被调度,真正实现并发。

理解了这个三角关系,就掌握了 asyncio 的基本工作原理:你用 async def 编写可能挂起的逻辑,用 asyncio.create_task 把这些逻辑注册进事件循环,事件循环则通过 await 处的让出机会,让多个任务交替执行,从而实现单线程下的高并发 I/O 处理。