前言
在日常开发中,经常会遇到需要同时处理多个 IO 任务的场景,比如:
- 同时请求多个接口
- 批量下载文件
- 并发查询数据库
如果使用传统的同步写法,任务只能一个接一个执行,总耗时就是所有任务耗时之和。而使用 Python 的 asyncio,可以在单线程内实现并发,大幅提升 IO 密集型任务的效率。
本文通过一个简单示例,带你快速入门 asyncio。
一、核心概念
asyncio 是 Python 标准库自带的异步 IO 框架,核心概念包括:
- 协程(Coroutine) :用
async def定义的函数 - 事件循环(Event Loop):负责调度协程执行
- 任务(Task):将协程包装成任务,交给事件循环调度
- await:挂起当前协程,等待异步操作完成
简单理解:遇到 IO 等待时,await 会让出控制权,让其他任务先执行。
二、同步 vs 异步
假设有三个下载任务,分别耗时 2 秒、1 秒、3 秒。
同步执行总耗时约:
text
2 + 1 + 3 = 6 秒
异步并发执行总耗时约:
text
max(2, 1, 3) = 3 秒
下面用代码验证。
三、完整示例
python
import asyncio
import time
async def download(name: str, delay: float):
print(f"[{name}] 开始下载...")
await asyncio.sleep(delay) # 模拟网络 IO
print(f"[{name}] 下载完成,耗时 {delay}s")
return f"{name} 的结果"
async def main():
start = time.perf_counter()
tasks = [
download("文件A", 2),
download("文件B", 1),
download("文件C", 3),
]
results = await asyncio.gather(*tasks)
end = time.perf_counter()
print("执行结果:", results)
print(f"总耗时: {end - start:.2f}s")
if __name__ == "__main__":
asyncio.run(main())
运行结果:
text
[文件A] 开始下载...
[文件B] 开始下载...
[文件C] 开始下载...
[文件B] 下载完成,耗时 1s
[文件A] 下载完成,耗时 2s
[文件C] 下载完成,耗时 3s
执行结果: ['文件A 的结果', '文件B 的结果', '文件C 的结果']
总耗时: 3.00s
可以看到,总耗时约等于最慢的任务,而不是所有任务之和。
四、控制并发数量
如果任务太多,一次性全部并发可能会导致资源耗尽。可以使用 Semaphore 限制并发数。
python
import asyncio
import time
sem = asyncio.Semaphore(2) # 最多同时执行 2 个任务
async def download(name: str, delay: float):
async with sem:
print(f"[{name}] 开始下载...")
await asyncio.sleep(delay)
print(f"[{name}] 下载完成")
return name
async def main():
start = time.perf_counter()
tasks = [download(f"文件{i}", 1) for i in range(1, 6)]
results = await asyncio.gather(*tasks)
end = time.perf_counter()
print("结果:", results)
print(f"总耗时: {end - start:.2f}s")
if __name__ == "__main__":
asyncio.run(main())
五、常见坑
-
忘记加
await协程函数调用后不会立即执行,必须用
await或包装成 Task。 -
在协程中使用
time.sleep()time.sleep()会阻塞整个事件循环,应该使用await asyncio.sleep()。 -
CPU 密集型任务不适合 asyncio
asyncio 适合 IO 密集型任务。如果是 CPU 密集型,建议使用多进程。
六、总结
asyncio 是 Python 中非常实用的异步编程工具,特别适合爬虫、API 聚合、文件下载等 IO 密集型场景。
记住三个关键词:
async def定义协程await等待异步操作asyncio.gather并发执行多个任务
掌握这些,就能写出高效又简洁的异步代码。