asyncio 用「协程 + 事件循环」实现单线程并发:在 IO 等待时让出控制权去执行别的协程,等待结束后再恢复。适合 IO 密集型任务,不适合 CPU 密集型。

核心抽象

概念作用
协程(Coroutine)async def 定义;遇到 await 暂停,让出执行权
事件循环(Event Loop)调度所有协程,由 asyncio.run 启动
任务(Task)包装协程提交给事件循环;用 asyncio.create_task() 创建

同步 vs 异步

# 同步:3 个任务依次执行,总耗时 3s
import time
 
def task(name):
    print(f"开始 {name}")
    time.sleep(1)
    print(f"结束 {name}")
 
start = time.time()
task("任务1"); task("任务2"); task("任务3")
print(f"总耗时: {time.time() - start:.2f}s")
# 异步:3 个任务并发,总耗时 ≈ 1s
import asyncio, time
 
async def task(name):
    print(f"开始 {name}")
    await asyncio.sleep(1)  # 让出执行权
    print(f"结束 {name}")
 
async def main():
    start = time.time()
    await asyncio.gather(task("任务1"), task("任务2"), task("任务3"))
    print(f"总耗时: {time.time() - start:.2f}s")
 
asyncio.run(main())

关键差异:time.sleep 阻塞线程,await asyncio.sleep 让出协程,事件循环立即切去运行其他协程。

Task 与 gather

Task — 把协程提交给事件循环并行运行:

async def main():
    t1 = asyncio.create_task(task("任务1"))
    t2 = asyncio.create_task(task("任务2"))
    await t1
    await t2

gather — 并发执行多个协程并按顺序收集返回值:

async def task(name):
    await asyncio.sleep(1)
    return f"{name} 完成"
 
async def main():
    results = await asyncio.gather(task("任务1"), task("任务2"))
    print(results)  # ['任务1 完成', '任务2 完成']

适用与不适用

场景类型推荐方案说明
IO 密集型(网络、磁盘、DB)asyncio + aiohttp/asyncpg/aiofiles等待时不浪费 CPU
CPU 密集型(计算、图像处理)multiprocessingasyncio 单线程,CPU 忙时无法切换

三个易踩的坑

  • 在协程里调用 time.sleep() 等阻塞函数会阻塞整个事件循环,必须用异步替代
  • 协程对象只是配方,没有 awaitcreate_task 不会执行
  • asyncio.run() 不能在已运行的事件循环里调用(如 Jupyter 中改用 await