# 协作式多任务:Python Asyncio 的核心 ## 协作式多任务:主动交替执行 与传统操作系统通过强制切换进程(抢占式多任务)不同,Python 的 asyncio 采用了一种完全不同的方法,称为**协作式多任务**。 ### 协作式多任务的工作原理 在协作式多任务中: - 任务会一直运行,直到它们通过 `await` 主动让出控制权 - 没有自动的时间片切换或强制抢占 - CPU 密集型任务必须手动让出控制权,否则会阻塞其他任务 - 如果某个任务不配合,可能会阻塞整个系统 可以把它想象成一组人讨论问题,每个人都同意只在自然停顿时发言,然后让别人说话。当大家都遵守规则时,这种方式非常有效,但如果有人一直占用话语权,就会破坏整个系统。 ### 抢占式 vs 协作式:关键区别 | 抢占式多任务(操作系统线程) | 协作式多任务(Asyncio) | |-------------------------------|-------------------------------| | 操作系统强制中断任务 | 任务主动让出控制权 | | 任务随时可能被中断 | 任务会一直运行直到遇到 await | | 系统定时器触发上下文切换 | 等待操作时触发切换 | | 适合 CPU 密集型任务 | 最适合 I/O 密集型任务 | | 需要复杂的同步机制 | 同步更简单 | | 任务切换自动发生 | 程序员需手动添加 await 点 | ### 篮球类比 想象一场篮球比赛: - **抢占式多任务** 就像有一个投篮计时器——时间一到,无论你在做什么,裁判(操作系统)都会强制收回球权 - **协作式多任务** 则像是街头篮球,靠自觉——大家投完篮或无法推进时主动传球 第二种方式只要大家都遵守规则就很顺畅,但只要有一个自私的球员,比赛就会变得糟糕。 ### 为什么 Python 选择协作式多任务 协作式多任务有几个优势: 1. **简单** —— 不需要锁等复杂的同步原语 2. **高效** —— 没有频繁的上下文切换带来的开销 3. **可预测** —— 任务在明确的点让出控制权 4. **单线程** —— 避免了许多线程相关的 bug 和竞态条件 缺点是程序员需要更加注意代码何时、何地让出控制权。 ### 使用 Asyncio 时需要注意的事项 由于 asyncio 依赖于任务间的协作,以下实践非常重要: - 使用 `aiofiles.open()` 替代普通的 `open()`,避免阻塞事件循环 - 进行 HTTP 操作时用 `aiohttp` 替代 `requests` - 延时操作时用 `asyncio.sleep()` 替代 `time.sleep()` - 在 CPU 密集型操作中定期让出控制权: ```python async def compute_intensive_task(): result = 0 for i in range(1_000_000): result += i * i # 每 10,000 次迭代让出一次控制权,允许其他任务运行 if i % 10000 == 0: await asyncio.sleep(0) # 睡眠 0 秒,仅用于让出控制权 return result ``` - 对于真正的 CPU 密集型任务,建议使用线程池: ```python async def run_in_thread(cpu_bound_function, *args): loop = asyncio.get_running_loop() return await loop.run_in_executor(None, cpu_bound_function, *args) ``` 理解并尊重 asyncio 的协作本质,你就能构建出高效且并发性强的应用程序,而无需传统多线程的复杂性。