Python asyncio.to_thread 实战:在协程里调用阻塞库不卡住事件循环
Python asyncio.to_thread 实战:在协程里调用阻塞库不卡住事件循环写了个 FastAPI 接口,里面调了一个只有同步版本的 SDK(比如某个老牌的图像处理库或数据库驱动),压测时 QPS 惨不忍睹,而且并发一上来所有请求都卡住。问题不在那个库慢,而在于你在协程里直接调了阻塞函数,把整个事件循环给堵死了。这篇讲清楚为什么会堵,以及asyncio.to_thread怎么救。先搞懂:一个阻塞调用堵死所有协程asyncio 是单线程事件循环。协程之间靠await主动让出控制权来实现并发。一旦某个协程执行了一个不会 await 的阻塞调用(time.sleep、requests.get、同步文件读写、CPU 密集计算),事件循环就被这一个协程霸占,其他所有协程全都得排队等它。看个能复现的例子:importasyncioimporttimedefblocking_io():time.sleep(2)# 模拟阻塞的同步库调用returnresultasyncdefworker(name):print(f{name}start)resultblocking_io()# 直接调阻塞函数——灾难print(f{name}done:{result})asyncdefmain():starttime.perf_counter()awaitasyncio.gather(worker(A),worker(B),worker(C))print(ftotal:{time.perf_counter()-start:.1f}s)asyncio.run(main())你以为三个 worker 并发跑,2 秒就完事?实际输出总耗时6 秒——因为blocking_io根本没让出事件循环,三个协程串行执行。更糟的是,如果这是个 Web 服务,这 6 秒里连健康检查接口都无法响应,整个服务像是死了。asyncio.to_thread:把阻塞调用甩到线程池Python 3.9 加入的asyncio.to_thread就是干这个的:它把同步函数丢到一个后台线程执行,并返回一个可以await的协程。当前协程在等待期间会让出事件循环,其他协程照常跑。importasyncioimporttimedefblocking_io():time.sleep(2)returnresultasyncdefworker(name):print(f{name}start)# to_thread 把 blocking_io 扔到线程池执行,await 期间事件循环自由resultawaitasyncio.to_thread(blocking_io)print(f{name}done:{result})asyncdefmain():starttime.perf_counter()awaitasyncio.gather(worker(A),worker(B),worker(C))print(ftotal:{time.perf_counter()-start:.1f}s)asyncio.run(main())这次总耗时2 秒,三个阻塞调用真正并行了。原理很简单:GIL 在遇到time.sleep这类 I/O 阻塞时会释放,所以多个线程能真正同时等待。带参数怎么传?位置参数直接跟,关键字参数用 functools.partialasyncio.to_thread(func, *args, **kwargs)支持透传参数:importasyncioimportfunctoolsdeffetch(url,timeout5,retries3):...# 某个只有同步版本的 http 库returnf{url}okasyncdefmain():# 位置参数和关键字参数都能直接传r1awaitasyncio.to_thread(fetch,http://a.com,timeout10)# 如果你要预先绑定一批参数再传,用 functools.partial 更清晰callfunctools.partial(fetch,timeout10,retries5)r2awaitasyncio.to_thread(call,http://b.com)print(r1,r2)asyncio.run(main())关键坑一:to_thread 救不了 CPU 密集任务to_thread用的是线程,而 Python 有 GIL。对于I/O 阻塞(sleep、网络、磁盘),等待时 GIL 会释放,多线程有效;但对于纯 CPU 计算(大循环、图像编解码、加解密),GIL 不会释放,多个线程还是抢同一个核,不会加速,甚至因线程切换更慢。defcpu_heavy(n):returnsum(i*iforiinrange(n))# 纯计算,GIL 全程持有# 这样并发跑 4 个,不会快 4 倍,基本等于串行awaitasyncio.gather(*[asyncio.to_thread(cpu_heavy,10**7)for_inrange(4)])CPU 密集要用进程,应该走loop.run_in_executor配ProcessPoolExecutor:importasynciofromconcurrent.futuresimportProcessPoolExecutorasyncdefmain():loopasyncio.get_running_loop()withProcessPoolExecutor()aspool:# 用多进程绕开 GIL,CPU 密集才能真正并行resultsawaitasyncio.gather(*[loop.run_in_executor(pool,cpu_heavy,10**7)for_inrange(4)])print(results)关键坑二:默认线程池只有很少的线程to_thread底层用的是事件循环的默认ThreadPoolExecutor,它的线程数默认是min(32, os.cpu_count() 4)。如果你同时甩几百个阻塞调用进去,超出线程数的会排队,并发度被悄悄限制。想要更大或独立的池,自己建一个,用run_in_executor指定:importasynciofromconcurrent.futuresimportThreadPoolExecutorasyncdefmain():loopasyncio.get_running_loop()# 为一批阻塞 I/O 单独开一个 50 线程的池,不和默认池抢资源withThreadPoolExecutor(max_workers50)aspool:tasks[loop.run_in_executor(pool,blocking_io)for_inrange(50)]awaitasyncio.gather(*tasks)关键坑三:取消不会真正停掉线程里的函数await asyncio.to_thread(...)被取消时,协程会抛CancelledError,但已经在线程里跑着的那个同步函数不会被中断——它会一直跑到自己结束,只是结果被丢弃。所以to_thread里的函数最好本身支持超时(比如给 http 库设timeout),别指望外层asyncio.wait_for能把它掐断。小结asyncio 是单线程,协程里直接调阻塞函数会堵死整个事件循环,并发退化成串行。asyncio.to_thread(func, *args, **kwargs)(3.9)把阻塞调用甩到线程池,当前协程await时让出循环,I/O 阻塞型任务能真正并行。CPU 密集别用它——GIL 拦着,要用run_in_executorProcessPoolExecutor走多进程。默认线程池线程数有限(约 32),大批量并发要自建ThreadPoolExecutor。取消协程停不掉已在跑的线程函数,超时得靠函数自己的 timeout 参数。一句话记忆点:I/O 阻塞用 to_thread,CPU 密集用 ProcessPool,别把线程当成绕开 GIL 的银弹。

相关新闻

【极简编程智能体 Pi Agent】 自定义扩展与高级技巧:打造专属 AI 编程工作流

【极简编程智能体 Pi Agent】 自定义扩展与高级技巧:打造专属 AI 编程工作流

文章目录 一、Pi 扩展系统概览 1.1 扩展能做什么? 1.2 扩展加载位置 1.3 可用导入包 二、编写第一个扩展 2.1 基本结构 2.2 异步工厂函数 2.3 扩展组织方式 三、扩展事件系统详解 3.1 生命周期全景 3.2 关键事件详解 四、注册自定义工具 五、Skills 技能系统 5.1 什么是 Skill…

2026/8/24 2:10:35 阅读更多 →
AI做销售是神器还是坑?我亲测三个月告诉你

AI做销售是神器还是坑?我亲测三个月告诉你

标题 AI做销售,到底是真香还是智商税? 正文 说实话, 去年, 当我着手开始认真去琢磨AI做销售这件事情的时候, 我的心里实际上是挺打鼓的。 身边有好多同行都在进行尝试了, 其中一些人兴奋到了极点, 声称效率提升了好几倍;而另外一些人则不停地…

2026/8/22 22:53:17 阅读更多 →
HMCL 启动器实战:从零开始在 10 分钟内跑通一个 Minecraft 模组整合包

HMCL 启动器实战:从零开始在 10 分钟内跑通一个 Minecraft 模组整合包

HMCL 启动器实战:从零开始在 10 分钟内跑通一个 Minecraft 模组整合包 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL 刚装机就栽跟头:装…

2026/8/22 22:53:17 阅读更多 →

最新新闻

基于ROS 2与MoveIt 2的机器人洗碗系统:从视觉感知到力控抓取全链路实践

基于ROS 2与MoveIt 2的机器人洗碗系统:从视觉感知到力控抓取全链路实践

在工业自动化和家庭服务机器人领域,洗碗一直被视为一项需要精细操作和复杂感知的任务。传统工业机器人依赖于精确的预编程路径和固定夹具,难以应对家庭厨房中形状各异、位置随机的碗碟。然而,随着AI视觉识别与力控协同技术的成熟,…

2026/8/24 2:10:38 阅读更多 →
三步跑通虚拟人视频生成:MuseV、MuseTalk、MusePose 怎么串成一条流水线

三步跑通虚拟人视频生成:MuseV、MuseTalk、MusePose 怎么串成一条流水线

三步跑通虚拟人视频生成:MuseV、MuseTalk、MusePose 怎么串成一条流水线 【免费下载链接】MuseV MuseV: Infinite-length and High Fidelity Virtual Human Video Generation with Visual Conditioned Parallel Denoising 项目地址: https://gitcode.com/GitHub_T…

2026/8/24 2:10:38 阅读更多 →
多智能体系统合谋审计:Colosseum框架设计与LLM智能体检测策略

多智能体系统合谋审计:Colosseum框架设计与LLM智能体检测策略

1. 项目概述:当多智能体系统“抱团”,我们如何审计?最近在搞多智能体系统(Multi-Agent Systems, MAS)的研究和落地,一个绕不开的“房间里的大象”就是合谋(Collusion)。想象一下&…

2026/8/24 2:10:38 阅读更多 →
大模型研究工程师:技能要求、面试准备与职业发展全解析

大模型研究工程师:技能要求、面试准备与职业发展全解析

如果你正在关注大模型领域的技术发展,可能会发现一个现象:顶尖LLM实验室的研究工程师(Research Engineer)岗位正在成为技术人才竞争的新焦点。与传统软件工程师或纯研究人员不同,这个角色需要同时具备工程实现能力和对…

2026/8/24 2:10:38 阅读更多 →
回测最后一天刚发出买入信号:没有下一交易日时怎样收尾

回测最后一天刚发出买入信号:没有下一交易日时怎样收尾

回测区间的最后一天刚好满足买入条件,报告里却没有对应交易,这种情况很常见。牛股王股票的策略条件、历史回测和交易明细能帮助普通投资者复核末端信号;聚宽可继续延长研究区间,PTrade一类券商侧工具还要面对账户和委托状态。先把…

2026/8/24 2:10:38 阅读更多 →
OfficeCLI 完整指南:3 行命令,让 AI 自动生成 Word、Excel 与 PPT

OfficeCLI 完整指南:3 行命令,让 AI 自动生成 Word、Excel 与 PPT

OfficeCLI 完整指南:3 行命令,让 AI 自动生成 Word、Excel 与 PPT 【免费下载链接】OfficeCLI OfficeCLI is the first and best Office suite purpose-built for AI agents to read, edit, and automate Word, Excel, and PowerPoint files. Free, open…

2026/8/24 2:09:37 阅读更多 →

日新闻

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践 前端安全依赖分层防护。没有任何单一配置能替代输出编码、权限校验和依赖更新。 把不可信内容当作数据 默认使用框架的转义能力;确需渲染 HTML 时,先在服务端或可信的客户端库中进行白名单过滤。避免把用户输入直接赋给 inne…

2026/8/24 1:08:15 阅读更多 →
Windows登录密码存储机制全解析:从哈希算法到安全加固实战

Windows登录密码存储机制全解析:从哈希算法到安全加固实战

1. 项目概述:Windows登录密码的“黑匣子”每次你按下CtrlAltDel,输入密码,然后看到那个熟悉的桌面,这背后发生了一系列复杂而精密的操作。作为一名长期与Windows系统打交道的从业者,我经常被问到:“我的密码…

2026/8/24 1:08:15 阅读更多 →
AI面试系统安全挑战与解决方案

AI面试系统安全挑战与解决方案

1. 项目概述:AI面试系统的安全挑战去年参与某跨国企业AI面试系统部署时,遇到一个典型案例:候选人在视频面试中无意提到竞争对手产品名称,系统竟自动将该信息关联到企业知识库并生成竞品分析报告。这个看似"智能"的功能&…

2026/8/24 1:08:15 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/24 0:06:02 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/24 0:20:20 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/24 0:14:11 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/23 18:47:06 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/23 12:10:44 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →