Featured image of post 协作式多任务: Python Asyncio 的核心
Python 并发

协作式多任务: Python Asyncio 的核心

了解 Python 的协作式多任务机制, 以及它与传统线程的区别

协作式多任务: 主动交替执行

与传统操作系统通过强制切换进程 (抢占式多任务) 不同, 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 密集型操作中定期让出控制权:
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 密集型任务, 建议使用线程池:
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 的协作本质, 你就能构建出高效且并发性强的应用程序, 而无需传统多线程的复杂性。

© 2022 - 2026 张欣耕

保留所有权利