asyncio事件循环单线程,同步阻塞操作(如time.sleep、requests、sqlite3)会冻结整个循环;async/await非魔法,需用aiohttp等异步库或run_in_executor规避;阻塞导致超时失效、任务堆积与资源泄漏。

因为 Python 的 asyncio 事件循环是单线程的,任何阻塞操作都会让整个循环卡住,所有协程全部停摆。
asyncio 事件循环根本不能并行执行 CPU 或同步 I/O
事件循环靠轮询和回调调度协程,它本身不创建新线程。一旦你在 async 函数里调用 time.sleep()、requests.get()、open().read() 或任意未适配异步的库,控制权就永久交出去了——没有回调、没有切换、没有“让出”,直到那个阻塞调用返回。
-
time.sleep(2)会让整个服务暂停 2 秒,哪怕你同时跑了 100 个协程 -
requests.get("https://api.example.com")在等待响应时,事件循环完全无法处理其他任务 - 同步数据库驱动(如
sqlite3.connect())打开连接或执行查询,同样冻结调度
常见误用:以为加了 async/await 就自动异步了
这只是语法糖,不改变底层行为。比如下面这段代码依然会阻塞:
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
async def bad_fetch():
import requests
return requests.get("https://httpbin.org/delay/3").json() # ❌ 同步阻塞
它被标记为 async,但内部调用的是纯同步函数。事件循环照常启动这个协程,然后卡在 requests.get 上,动弹不得。
- 真正异步的替代方案:用
aiohttp、httpx.AsyncClient、aiomysql、aiofiles - 没现成异步库?必须显式移交到线程池:
await loop.run_in_executor(None, sync_func, *args) - 别把
async def当装饰器——它不魔法转化同步代码
阻塞导致的连锁反应:超时失效、任务堆积、资源泄漏
一个协程阻塞,不仅自己卡住,还会拖垮所有依赖它的逻辑:
-
asyncio.wait_for(task(), timeout=1)对阻塞调用无效——超时异常可能根本抛不出,因为事件循环没机会检查是否超时 - 用
asyncio.gather()并发多个任务?只要其中一个是同步阻塞,其他任务全得陪等 - 数据库连接未关闭、文件句柄未释放,往往是因为阻塞导致
async with的__aexit__根本没执行到
真正难察觉的点不是“哪里写了阻塞”,而是“哪里漏掉了异步适配”——比如日志写入用了 logging.info()(同步),配置加载用了 json.load(open(...))(同步),甚至第三方 SDK 内部偷偷调了 subprocess.run()(同步)。这些地方不会报错,但会在高并发下突然集体变慢。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










