Skip to content

先分清几个对象

学 asyncio 时最容易混的是 coroutine、Future、Task。先不用急着背定义,可以把它们放到一个实际调用里看:

python
async def fetch_user(user_id):
    await asyncio.sleep(1)
    return {"id": user_id}

task = asyncio.create_task(fetch_user(42))
user = await task

fetch_user(42) 只是创建了一个“以后可以执行的协程对象”。create_task() 把它交给事件循环。await task 等它完成并拿结果。

一句话版本

概念它是什么你什么时候接触它
coroutine functionasync def 定义的函数写业务逻辑时
coroutine object调用 coroutine function 后得到的对象main()worker() 的返回值
awaitable可以被 await 的对象coroutine、Task、Future 都是常见 awaitable
Future一张“以后会有结果”的占位符框架和库内部代码更常直接使用
Task被事件循环调度的 coroutineasyncio.create_task()TaskGroup.create_task()
event loop负责安排谁先运行、谁继续等asyncio.run() 内部创建和关闭
Handle被放到 ready 队列里的 callback读事件循环源码时会遇到
Transport/Protocolcallback 风格的网络接口框架作者更常用
StreamReader/StreamWriterawait 读写网络连接的接口应用代码更常用

调用 async def 不会执行函数体

python
async def hello():
    print("hello")

coro = hello()
print(coro)

上面不会打印 hello。调用 hello() 只是拿到一个 coroutine object,函数体还没开始跑。要让它执行,你需要:

  • await hello(),由当前 Task 进入它。
  • asyncio.run(hello()),创建事件循环并运行入口。
  • asyncio.create_task(hello()),把它包装成 Task 并交给事件循环调度。

Task 是被调度的协程

没有 Task,coroutine object 就像一份还没排上号的工作。创建 Task 后,它才进入事件循环的队列,后面会被安排执行。

Future 是结果容器

Future 不负责计算。它只记录一件事:结果现在有没有准备好。

text
pending -> done(result)
pending -> done(exception)
pending -> cancelled

Task 也会有结果、异常和取消状态,所以它看起来很像 Future。差别是:

  • Future 只等别人把结果填进来。
  • Task 包着一个 coroutine,会自己一步步把它跑下去。

事件循环负责什么

事件循环不是魔法。它反复做几件很普通的事:

职责典型 API内部数据结构
立刻调度 callbackloop.call_soon()ready 队列
延迟调度 callbackloop.call_later()asyncio.sleep()定时器堆
继续运行 Taskcreate_task()Task 的 _step callback
等待 I/O 就绪streams、socket、subprocessselector 或平台事件机制
管理生命周期asyncio.run()Runner创建、运行、关闭 loop

await 做了什么

从使用者角度看:

python
value = await something()

意思是:如果 something() 还没结果,当前协程先停下。事件循环这时可以去处理别的任务。等 something() 有结果后,再回到这一行继续。

从实现角度看,大致是这样:

写应用时先用哪些 API

写应用时,先用这些:

  • asyncio.run()
  • asyncio.create_task()
  • asyncio.TaskGroup
  • asyncio.gather()
  • asyncio.timeout()
  • asyncio.Queue
  • asyncio.open_connection()
  • asyncio.start_server()

下面这些更多是库和框架会碰到,入门时不用急:

  • loop.call_soon()
  • loop.call_later()
  • loop.create_future()
  • loop.add_reader()
  • loop.create_connection()
  • Transport/Protocol

如果你只是写业务代码,通常不需要手动调用 loop.call_soon()loop.create_future()。等你要把一个 callback 风格的网络库接进 asyncio,再看这些低层 API 会更容易理解。

小练习

回答下面问题:

  1. async def f()f() 分别是什么?
  2. create_task(f()) 做了哪两件事?
  3. Future 和 Task 的共同点是什么,差别是什么?
  4. 为什么 time.sleep() 放进 async def 会堵住所有 Task?

面向学习目的的 Python asyncio 中文教程与 mini_asyncio 教学运行时。