Python异步服务依赖事件循环、协程和非阻塞IO协同工作,核心是避免单线程被IO阻塞;async/await用于定义和等待协程,需配合异步库(如httpx、asyncpg)和正确并发控制(如Semaphore、gather),误用同步代码会拖垮性能。

Python异步服务不是靠多线程或多进程堆出来的,而是靠事件循环 + 协程 + 非阻塞IO协同工作的。核心在于让单个线程不被IO卡住,持续处理其他任务。
理解async/await和协程的本质
async def定义的函数不是普通函数,它返回一个协程对象(coroutine object),只有被事件循环调度执行时才会真正运行。await只能出现在async函数里,且后面必须是可等待对象(如另一个协程、asyncio.Future、asyncio.Task,或实现了__await__的对象)。
常见误区:
– 把同步代码直接加async/await不会变快;
– await time.sleep(1)会报错,得用await asyncio.sleep(1);
– 没有await的协程不会执行,就像定义了函数但没调用。
简单例子:
import asyncioasync def fetch_data(): print("开始请求") await asyncio.sleep(1) # 模拟非阻塞网络延迟 return "数据已就绪"
async def main(): result = await fetch_data() # 这里才真正执行并等待 print(result)
启动事件循环
asyncio.run(main())
立即学习“Python免费学习笔记(深入)”;
用asyncio构建基础异步HTTP服务
推荐用Starlette或FastAPI(底层基于Starlette+Pydantic),它们原生支持async视图,比Flask/Tornado更轻量、更现代。
关键点:
– 路由函数声明为async def;
– 数据库操作要用异步驱动(如asyncpg、aiomysql、tortoise-orm);
– 文件读写避免open(),改用aiofiles;
– 不要混用阻塞调用(如requests.get、json.loads大文件),否则会拖垮整个事件循环。
示例(FastAPI):
from fastapi import FastAPI import httpxapp = FastAPI()
@app.get("/items/") async def read_items(): async with httpx.AsyncClient() as client: resp = await client.get("https://www.php.cn/link/c2148796071914983ed6b6e9dbbff735") return resp.json()
手动管理事件循环与并发控制
日常开发中多数用asyncio.run()就够了,但在嵌入式环境、测试或与同步框架(如Django)集成时,可能需要更精细控制。
常用技巧:
– asyncio.create_task()把协程包装成Task,立即调度,适合“发出去就不用等”的场景;
– asyncio.gather()并发运行多个协程,全部完成才返回结果;
– asyncio.wait()更灵活,可设timeout、first_completed等策略;
– 用asyncio.Semaphore限制并发数,防爆资源(比如同时最多5个数据库连接)。
并发限流示例:
sem = asyncio.Semaphore(3)async def limited_fetch(url): async with sem: # 最多3个同时执行 async with httpx.AsyncClient() as client: return await client.get(url)
避开常见陷阱
异步不是银弹,用错反而更慢甚至出错:
- 别在async函数里调用time.sleep()、print()太多(虽不阻塞,但高频IO影响性能)
- 不要用threading.Lock,换asyncio.Lock;不要用queue.Queue,换asyncio.Queue
- 数据库连接池必须用异步驱动,否则一个await就把整个循环卡死
- 调试时print位置很重要——协程未await前只是对象,print不会触发执行
- 日志建议用structlog或logging配置异步handler,避免阻塞
基本上就这些。异步IO本身不复杂,难的是打破同步思维惯性,养成“哪里可能阻塞,就查它有没有async版本”的习惯。










