商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > 为什么Python异步函数中不能包含过于复杂的计算密集型逻辑?

为什么Python异步函数中不能包含过于复杂的计算密集型逻辑?

  发布于2026-07-11 阅读(0)

扫一扫,手机访问

Python异步编程:为什么CPU密集型任务会让你的async代码“卡死”?

先给出一个明确结论:在async函数里执行CPU密集型逻辑,会直接阻塞事件循环,让所有协程都陷入等待状态——异步编程带来的并发能力会瞬间归零。

问题根源:async函数里做计算会卡住事件循环

asyncio的事件循环本质上是单线程的,采用协作式调度机制。这意味着,只要某个协程不主动执行await,它就会一直占据CPU,其他协程根本没机会运行。

  • 举个典型例子:在async def里写sum(range(10**8))或者进行矩阵运算,这几十毫秒甚至几百毫秒内,整个服务对网络请求、数据库查询、定时任务都会完全无响应
  • 表现出来的现象非常直观:QPS断崖式下跌、超时激增、监控显示事件循环延迟(loop.time()与实际耗时不匹配)
  • 这和用time.sleep(5)卡住的情况同样严重——只是后者更容易被察觉,前者容易被误认为“只是慢了一点”

为什么用asyncio.run_in_executor简单包一层不解决问题?

很多开发者会想到用run_in_executor来包装,但这种方法并非“随便包一下就能用”,很多写法反而会引入新问题。

  • run_in_executor(None, ...)默认走ThreadPoolExecutor,而CPU密集型任务在线程池里跑,受GIL限制,多线程并行效果极差
  • 正确做法必须使用ProcessPoolExecutor,但这里有几个关键点容易踩坑:if __name__ == "__main__":在Windows上不是可选项,而是硬性要求,否则子进程会启动失败
  • 被调用的函数必须定义在模块顶层(不能是类方法、闭包或lambda),否则序列化时会报PicklingError
  • 大量小任务反复提交给进程池,IPC开销可能比计算本身还高;应该尽量批量处理,避免单个数字反复调用

如何判断一段逻辑是否适合放在async函数里?

判断标准其实很简单:看它有没有真正意义上的“等待点”。

  • await调用(如aiohttp.ClientSession.get()asyncpg.fetch())→ 安全
  • 只有纯Python循环、数学运算、字符串处理、字典遍历 → 不适合,应该剥离出去
  • 调用了C扩展但内部仍做大量计算(如某些NumPy操作未释放GIL)→ 表面看起来快,实则仍然阻塞,需要通过threading.current_thread().ident是否变化来实测验证

这里有一个最容易被忽略的陷阱:即使你把计算逻辑抽出去用ProcessPoolExecutor处理,如果结果要反序列化回主进程再做大量JSON序列化或模板渲染,这部分又会变成新的CPU瓶颈。异步不是魔法,它只解决“等待”的问题,并不能加速“计算”本身。

本文转载于:https://www.php.cn/faq/2799878.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注