面试准备

最近改动

更新页面 Python 并发语法

页面 kb/python-concurrency · 修改(rev 13) · cc · 2026-10-25 23:17

body

@@ -557,5 +557,4 @@
 
 ```python
-
 class Box:                               # 一个最小的「可以被 await 的东西」
     def __init__(self):
@@ -569,17 +568,17 @@
 
 async def demo():
-    print("1. 协程开始,跑到 await 之前")
+    print("1. 协程里:开始,跑到 await 之前")
     x = await box
-    print("3. 协程恢复了,x =", x)
+    print("3. 协程里:恢复了,x =", x)
 
 coro = demo()                            # 只建了协程对象,函数体一行都没执行
-print("0. 建好了协程对象:", type(coro).__name__)
+print("0. 外面:建好了协程对象")
 y = coro.send(None)                      # 跑到 yield 那里停下,yield 出来的东西就是 send 的返回值
-print("2. send 返回了:", "box" if y is box else y)
+print("2. 外面:send 返回了", "box" if y is box else y)
 box.done, box.value = True, 42           # 相当于 set_result(42)
 try:
     coro.send(None)                      # 从 yield 下一行接着跑
 except StopIteration:
-    print("4. 协程跑完了,第二次 send 抛 StopIteration")
+    print("4. 外面:第二次 send 抛了 StopIteration,协程跑完了")
 ```
 
@@ -587,18 +586,22 @@
 
 ```
-0. 建好了协程对象: coroutine
-1. 协程开始,跑到 await 之前
-2. send 返回了: box
-3. 协程恢复了,x = 42
-4. 协程跑完了,第二次 send 抛 StopIteration
-```
-
-逐行看:
-
-- 调 `demo()` 不执行函数体,只返回一个协程对象。第 0 行打印时,第 1 行还没打印。
-- 第一次 `send(None)` 从函数开头跑起,打印第 1 行,然后遇到 `await box`,进到 `box.__await__()` 里,执行到 `yield self` 停下。协程的局部变量和执行位置都保存在协程对象里,`yield` 出来的 `box` 成了 `send` 的返回值,所以第 2 行打印出 box。
-- 第二次 `send(None)` 从 `yield` 的下一行接着跑。这时 `box.done` 已经是 True,`__await__` 执行 `return self.value`,这个返回值就是 `await box` 整个表达式的值,所以 `x = 42`。函数跑完,`send` 抛 `StopIteration`。
-
-协程自己没法恢复自己,谁调 `send`,谁就决定它什么时候接着跑。事件循环要做的,就是在合适的时候替它调 `send`。
+0. 外面:建好了协程对象
+1. 协程里:开始,跑到 await 之前
+2. 外面:send 返回了 box
+3. 协程里:恢复了,x = 42
+4. 外面:第二次 send 抛了 StopIteration,协程跑完了
+```
+
+1 和 3 是协程 `demo` 里面打印的,0、2、4 是协程外面那几行打印的。按执行的先后,控制权在外面和协程里面之间来回切换:
+
+| 顺序 | 外面的代码 | 协程 `demo` 里面 |
+| --- | --- | --- |
+| 1 | `coro = demo()` 只建了协程对象,然后打印 0 | 还没开始,一行都没执行 |
+| 2 | 调 `coro.send(None)`,把控制权交给协程 | 从头开始跑,打印 1,执行到 `await box`,进到 `box.__await__` 里,在 `yield self` 处停下。局部变量和执行位置都存在协程对象里 |
+| 3 | `send` 返回了 `box`,就是 `yield` 出来的那个值,打印 2。然后把 `box.done` 设成 True、`box.value` 设成 42 | 停着不动 |
+| 4 | 再调一次 `coro.send(None)` | 从 `yield` 的下一行接着跑:`return self.value` 让 `await box` 的值是 42,所以 `x = 42`,打印 3,函数结束 |
+| 5 | 这次 `send` 抛出 `StopIteration`,外面接住它,打印 4 | 已经结束 |
+
+协程自己没法恢复自己,谁调 `send`,谁就决定它什么时候接着跑。事件循环做的,就是表里第 2 步和第 4 步「调 `send`」的那个角色。
 
 #### 一个最小的事件循环