一、核心基础概念
Python 异步基于 asyncio 标准库,核心要素:
- 协程函数 :用
async def定义,调用后不会立即执行,返回协程对象 - await:挂起当前协程,等待 IO 操作完成,期间事件循环可以调度其他协程
- 事件循环(EventLoop):异步程序的调度核心,负责轮询 IO、切换协程
- 任务(Task):将协程封装为可被事件循环调度的对象,实现并发
关键原则:异步代码中严禁调用同步阻塞接口 (
requests/time.sleep/普通文件读写等),会阻塞整个事件循环。
二、入门:基础语法 & 简单示例
1. 最简协程演示
python
import asyncio
# 定义协程函数
async def say_hello(name, delay):
print(f"开始: {name}")
# 异步休眠(非阻塞),代替 time.sleep
await asyncio.sleep(delay)
print(f"结束: {name}")
# 主入口
async def main():
# 串行执行
await say_hello("A", 1)
await say_hello("B", 1)
# 运行异步程序(Python 3.7+ 标准写法)
if __name__ == "__main__":
asyncio.run(main())
2. 实现并发:asyncio.create_task + gather
想要并发执行,必须把协程包装为 Task:
python
import asyncio
async def say_hello(name, delay):
print(f"开始: {name}")
await asyncio.sleep(delay)
print(f"结束: {name}")
async def main():
# 创建任务,立即交由事件循环调度
task1 = asyncio.create_task(say_hello("A", 1))
task2 = asyncio.create_task(say_hello("B", 1))
# 等待所有任务完成
await task1
await task2
# 等价简写:asyncio.gather 批量并发
async def main2():
tasks = [
say_hello("A", 1),
say_hello("B", 1)
]
# 并发执行所有协程,收集返回结果
results = await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(main())
- 串行总耗时:约 2s
- 并发总耗时:约 1s
三、主流场景实战(生产常用)
场景1:异步网络请求(爬虫/接口调用)
使用 aiohttp(异步 HTTP 客户端),先安装:
bash
pip install aiohttp
基础并发请求
python
import asyncio
import aiohttp
# 单个请求协程
async def fetch(session: aiohttp.ClientSession, url: str):
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
status = resp.status
text = await resp.text()
return f"[{status}] {url} 长度:{len(text)}"
except Exception as e:
return f"[失败] {url} 错误:{str(e)}"
async def main():
urls = [
"https://httpbin.org/get",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2"
]
# 复用 Session(官方推荐,减少连接开销)
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
# 并发执行
res_list = await asyncio.gather(*tasks)
for res in res_list:
print(res)
if __name__ == "__main__":
asyncio.run(main())
限制最大并发(防封禁/压垮服务)
使用 Semaphore 信号量 控制并发数:
python
import asyncio
import aiohttp
# 限制最大 2 个并发
SEMAPHORE = asyncio.Semaphore(2)
async def fetch(session: aiohttp.ClientSession, url: str):
async with SEMAPHORE: # 进入前抢占信号量
async with session.get(url) as resp:
return f"{url} -> {resp.status}"
async def main():
urls = ["https://httpbin.org/get"] * 10
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, u) for u in urls]
results = await asyncio.gather(*tasks)
for r in results:
print(r)
asyncio.run(main())
场景2:异步文件 IO
标准库无原生异步文件,使用 aiofile:
bash
pip install aiofile
python
import asyncio
import aiofile
async def write_file(path, content):
async with aiofile.async_open(path, "w", encoding="utf-8") as f:
await f.write(content)
async def read_file(path):
async with aiofile.async_open(path, "r", encoding="utf-8") as f:
return await f.read()
async def main():
# 并发写文件
write_tasks = [write_file(f"test_{i}.txt", f"内容{i}") for i in range(5)]
await asyncio.gather(*write_tasks)
# 并发读文件
read_tasks = [read_file(f"test_{i}.txt") for i in range(5)]
contents = await asyncio.gather(*read_tasks)
print(contents)
asyncio.run(main())
场景3:异步数据库(MySQL 示例)
使用 aiomysql:
bash
pip install aiomysql
python
import asyncio
import aiomysql
async def db_query():
conn = await aiomysql.connect(
host="127.0.0.1",
port=3306,
user="root",
password="你的密码",
db="test_db"
)
cur = await conn.cursor()
await cur.execute("SELECT 1")
print(await cur.fetchone())
await cur.close()
conn.close()
asyncio.run(db_query())
四、异常、超时、重试 工程增强
1. 单个任务设置超时
python
async def demo():
# 超过 2 秒自动抛出超时异常
try:
await asyncio.wait_for(asyncio.sleep(3), timeout=2)
except asyncio.TimeoutError:
print("任务超时")
asyncio.run(demo())
2. gather 异常处理
return_exceptions=True:捕获异常,不中断整体任务
python
tasks = [task1, task2, task3]
results = await asyncio.gather(*tasks, return_exceptions=True)
五、关键避坑(高频问题)
-
不要混用同步阻塞代码
禁止在
async def里用:requests、time.sleep()、普通open()、同步数据库驱动。替换方案:
time.sleep→asyncio.sleeprequests→aiohttp- 普通文件 →
aiofile - 同步 DB → 对应异步驱动
-
不要在循环外创建大量 Session/连接
HTTP、数据库连接复用单例连接池,不要每个任务新建连接。
-
CPU 密集型不适合异步
异步单线程,CPU 计算会阻塞事件循环。计算密集型用
multiprocessing多进程。 -
老 Python 版本兼容
Python3.7+ 推荐
asyncio.run();3.6 及更早需手动管理事件循环。
六、整体使用流程总结
- 导入
asyncio,用async def定义协程函数 - IO 操作全部使用异步专用库 ,搭配
await - 通过
asyncio.create_task()/asyncio.gather()实现并发 - 用
Semaphore限制并发、wait_for做超时、捕获异常 - 入口统一使用
asyncio.run(main())启动事件循环
七、拓展:异步 Web 框架
如果是开发高并发 Web 服务,直接基于异步生态:
- FastAPI(主流,高性能、易上手)
- Sanic / Tornado(老牌异步框架)
底层依然基于asyncio事件循环。