-
我把一个 async 方法的返回类型写成了 async void,它里面抛的异常 try-catch 死活拦不住、还直接把整个进程干崩了:一次 C# async void 吞掉异常、让异常逃逸到顶层崩溃进程的深度复盘
我们有个后台服务,我图省事把一个普通异步方法的返回类型写成了 async void,还在调用处包了 try-catch 自以为万无一失。可一旦方法内部抛异常,诡异的事发生了:try-catch 根本没拦住、异常像幽灵穿透了它,而且未捕获的异常直接让整个进程崩溃退出。查到底才明白:async void 是专为事件处理器(签名要求 void)设计的、不该用在普通异步方法上;它无法被 await,调用处…- 0
- 0
-
一个在 forEach 回调里写 await 的批处理脚本,以为会一个个等着处理完,结果还没处理完就往下走、还吞掉了异常:一次 JavaScript 异步遍历的深度复盘
数据迁移脚本用 forEach 遍历、在回调里 await 逐条同步,处理完打印'全部同步完成'。结果那句几乎立刻就打印了(其实没同步完),而且某条失败抛的异常外层 try-catch 一个都没抓到、变成了 UnhandledPromiseRejection。根因是 Array.prototype.forEach 是同步遍历、完全忽略回调返回的 Promise、不会等待 awa…- 0
- 0
-
一个被随手写成 async void 的方法抛了异常,我外层的 try-catch 一个都没抓到、进程直接崩溃退出:一次 C# 异步返回类型用错的深度复盘
后台服务时不时毫无征兆整个进程挂掉,日志只留一条 UnhandledException——而那段抛异常的代码明明被 try-catch 严严实实包着。根因是被调方法返回类型是 async void 而非 async Task:async void 没有可 await 的 Task,异常无处传回,被直接抛到 SynchronizationContext 顶层成为未处理异常、崩溃进程,外层 try-c…- 2
- 0
-
我在一个异步方法上用 .Result 同步等了一下结果,整个请求就这么永远卡死了,没有任何报错也没有超时,我对着 async 配 .Result 造成的死锁这个坑排查大半天的复盘
一个让我对 C# async/await 从会用到敬畏的经典坑,折磨在它不报错不抛异常不超时,只是静悄悄永远卡死在那,请求转圈到天荒地老、日志却干干净净。老项目里要在一个同步方法里调一个异步方法拿数据,图省事没把整条链改异步,直接在异步方法后加了 .Result 想同步等结果:return GetDataAsync().Result。这在控制台可能正常,但放到旧版 ASP.NET、WPF、WinF…- 0
- 0
-
我的 C# 异步方法明明用 try-catch 包住了,里面抛的异常却直接崩了整个进程、catch 根本没拦住,我对着 async void 排查了大半天的复盘
后台服务里的事件处理逻辑,有个异步方法处理事件,我很小心地在调用处用 try-catch 包住,自以为万无一失。可生产上一旦方法内部抛异常整个进程就直接崩溃,try-catch 像不存在一样什么都没捕获到。盯着代码百思不得其解:异常明明在 try 块里抛的 catch 凭什么没接住?排查大半天才发现罪魁是个不起眼的细节——我把异步方法返回类型写成了 async void 而不是 async Tas…- 0
- 0
-
我在 C# 里随手把异步方法写成了 async void,结果里面抛的异常我的 try-catch 怎么都抓不到、还直接把整个进程干崩了,我排查了大半天的复盘
我顺手把一个业务异步方法写成了 async void,平时没事,直到某天它内部异步调用抛了异常——我在调用处用 try-catch 严严实实包着,异常却完全没被 catch 到,还直接把整个进程干崩了。深挖才懂全是 async void 的锅:它无法被 await,调用方调用后立刻往下走,等 100ms 后异步操作真正抛异常时,外层代码早执行完、离开那个 try-catch 了;更致命的是 asy…- 0
- 0
-
我的 asyncio 服务接口全是 async/await,QPS 却低得离谱,我以为是部署配置问题,最后揪出一个同步的 requests 调用,把整个单线程事件循环死死卡住的深度复盘
我用 asyncio + FastAPI 写了个服务,接口全是 async def,每处该 await 的都 await 了,自以为是纯异步高并发。可压测时 QPS 低得怀疑人生,100 个并发像排队过独木桥一样串行处理。我一度怀疑 uvicorn worker 数、想上多进程,扒代码才发现罪魁祸首:一个 async 接口里,我图方便调了同步阻塞的 requests.get——它把整个单线程事件循…- 0
- 0
-
忘了一个 await:TS 浮动 Promise 乱序吞错避坑
有个用 TypeScript 写的下单流程:扣库存、写订单、再发通知,每步封装成 async 函数主流程里一个个调过去,代码写得很顺。可上线后偶发诡异:有时订单写进去了库存却没扣、有时通知发了订单还没落库,还有一次某步明明内部抛了异常外层 try/catch 却完全没捕获到、错误像人间蒸发既没日志也没告警只留一堆对不上的脏数据。逻辑明明是顺序写的怎么会乱序还吞异常?逐行抠 async 调用才发现真…- 6
- 0
-
一行 .Result 拖垮整个应用:C# 异步死锁避坑
一个平时跑得稳稳的 ASP.NET 老项目,我给某接口加了点新功能、调用了异步 HTTP 客户端取下游数据,本地测一切正常,压测时却出了鬼:并发一上来接口就大面积超时,不是慢,是彻底卡死永远不返回,而且会蔓延,最后整个应用线程池仿佛被冻住,连其它正常接口也没了响应,重启能缓解、一压又复现。我先怀疑下游慢、线程池太小,抓 dump 一看线程没在干活,而是齐刷刷阻塞在同一行我自以为无害的代码:var …- 0
- 0
-
请求集体卡顿、单核打满:Node.js 事件循环阻塞避坑
一个平时快得没存在感的 Node.js 服务,偶尔会"集体卡顿"——几乎所有接口在同一瞬间一起超时几秒,然后又一起恢复,像什么都没发生。病来得没规律,日志翻不出半个 ERROR,内存也稳,唯一可疑的是卡顿那几秒总有一个 CPU 核心瞬间被打满到 100%,其它核都闲着。这个"单核打满、其它核闲置"的画面,正是 Node 单线程特性在喊话:某个请求里藏着一坨不…- 0
- 0
-
压测一上量接口全超时:C# async/await 死锁与线程池饥饿
一次本该平平无奇的上线前压测,把并发拉到几百之后整个服务就像被掐住了脖子:响应时间从几十毫秒飙到十几秒,最后大面积超时——可机器的 CPU 占用还不到 20%,内存也宽裕得很。这种"不忙却瘫"的景象比 CPU 打满还让人发毛。排查一圈数据库和下游都没问题,直到拉出线程池指标才发现真相:工作线程被一口气全占满,而它们没一个在干活,全都阻塞在一个 await 上睡觉等结果。凶手是一…- 2
- 0
-
C# async/await 死锁实战:一个 .Result 如何让接口集体卡死
一个 ASP.NET 服务在压力上来时会偶发性地卡死——不是崩溃,是卡住,请求全部超时,线程数飙到几百,CPU 却低得可怜,像是所有线程都在睡觉。我排查了整整两天,最后的修复只是删掉了一个单词:.Result。这篇就从这个经典死锁讲起,把 async/await 真正讲透:它到底是什么(剧透:它不是多线程)、.Result 死锁是怎么互相等待锁死的、ConfigureAwait(false) 在防…- 0
- 0
-
C# async/await 踩坑实战:同步阻塞死锁、async void 与线程池饥饿
事情是从一个看起来人畜无害的 PR 开始的:一位同事在 ASP.NET Core 控制器里图省事,把异步调用写成了 var data = GetDataAsync().Result;,能编译、本地点一下也出结果,评审就这么过了——结果一上生产,这个接口在并发上来后开始大面积超时,最后整个应用线程池被拖垮、几乎所有请求一起卡死,根因就是这一行同步阻塞异步。那次之后我把这些年在 C# 异步上踩过的坑系…- 0
- 0
-
从同步阻塞思维写一个高并发服务把所有 IO 都让线程死死占着一步步往下执行直到彻底完成、还为图省事在同步方法里用 .Result 等异步调用埋下 sync-over-async 反模式、一次促销流量只比平时高三四倍这种占着线程啥也不干的死等调用大量出现就把 IIS 线程池迅速占满耗尽线程池一空整个应用再没线程处理任何新请求守着 32 核服务器 CPU 闲在 5% 而所有 API 集体超时挂死 + 处理字符串数组挥霍内存毫无知觉解析报文用 Substring 一刀刀切每次都实打实分配新字符串拷贝一份拼接直接加号怼字节数组动不动 new 再 Array.Copy 在高频热路径上极短时间制造海量用完即弃的短命临时对象把 GC 喂到频繁暂停服务成片卡顿延迟毛刺 + JSON 序列化清一色 Newtonsoft 一把梭它靠运行时反射每次都探查类型动态读值在每秒序列化成千上万对象的高频服务里反射开销高居 CPU 热点榜还造一堆装箱临时垃圾反过来加剧 GC 卡顿 + 用 LINQ 却以为写下 Where 那行查询就执行完了根本不懂延迟执行把查询赋给变量先 Count 再 foreach 后 Any 同一查询被完整重跑三四遍又在循环里访问关联属性触发 N+1 一百个订单打一百零一次库直接打爆数据库 + 写服务类要用数据库 HTTP 日志就直接在类里 new 一个出来依赖被 new 死在内部绑死具体实现想换实现想测试注入 mock 都做不到测试只能连真库又慢又脆还自己手搓 static 单例埋多线程竞态 + 配置全堆 web.config 用 ConfigurationManager 字符串键去取键散落十几个文件改名漏一个就错把 SmtpHost 敲成 SmtpHsot 编译器毫不知情上线取出 null 才炸取出全是字符串还得自己 Parse + 引用类型默认可空类型完全不透露可空信息拿个参数无从知道会不会传 null 访问 order.Customer.Name 全凭运气 NullReferenceException 成了线上最阴魂不散的异常编译期毫无征兆全到线上特定路径才轰然爆发 + 应用被 .NET Framework 死死绑在 Windows 上只能上 Windows Server 装特定版本运行时还和别的应用共享互相牵制要配 IIS 管应用池环境稍不一致就诡异出错想上 Linux 容器享受云原生弹性精简门都没有 → 2026 现代 .NET 8 对所有 IO 用 async/await 全程异步 await 时线程被释放归还线程池服务别的请求同样线程数撑起高一个数量级并发 + Span 与 ReadOnlySpan 零拷贝切片 ArrayPool 租借复用缓冲把热路径分配压到极低 GC 压力骤降 + System.Text.Json 源生成器编译期生成直达序列化代码运行时零反射快且低分配还通 AOT + 吃透延迟执行该物化时一次 ToList 用 Include 预加载与 Select 投影根治 N+1 收敛成一次往返 + 内置 DI 容器构造函数注入只依赖接口可注入 mock 测试生命周期 Singleton/Scoped/Transient 由容器统一托管 + IConfiguration 加 IOptions 把一组配置强类型绑定到配置类属性强类型编译期可查分组归属清晰 + 开启可空引用类型把可空与否写进类型编译器流分析在编译期就揪出潜在 null 解引用逼你判空 + 迁到 .NET 8 跨平台用内置 Kestrel 宿主 self-contained 把运行时打包进产物容器把应用连同环境封成自给自足镜像一次构建哪都能跑接入 K8s 弹性伸缩 87 天战役复盘:47 套工程修法 + 7 个 P0 复盘 + 6 条工程哲学
8 人的 .NET 团队 87 天把一套支撑公司核心业务、在 Windows Server 加 IIS 上跑了八年、从当年整洁的 MVC 长成一坨同步阻塞反射横飞到处 new 配置散落空引用满天飞的 .NET Framework 4.x 祖传 C# 应用,系统性地现代化到 .NET 8——把我们彻底打醒的是一次再普通不过的流量高峰,促销带来的并发只比平时高三四倍本不该出事,可代码里一处在同步方法里…- 2
- 0
-
CompletableFuture 踩坑:异步任务静默失败,异常被吞了的复盘
商品聚合详情接口用 CompletableFuture 并发调下游,上线后页面偶发少一块内容,接口却返回 200、日志干净。排查发现是异常被 future 静默吞掉、getNow 没等结果取到 null、共用 commonPool。几天治理:exceptionally 接异常、独立线程池、allOf 等齐 + orTimeout 降级、MDC/TTL 传上下文。- 0
- 0
-
C# async/await 深度解析:异步编程背后到底发生了什么
async / await 是 C# 里最好用、也最容易"用错"的语法。好用,是因为它让异步代码长得几乎和同步代码一样直白;容易用错,是因为很多人只学了"怎么写",没搞懂"它背后到底做了什么" —— 于是写出会死锁的代码、会吞掉异常的代码、明明能并行却串行的代码。这篇文章把 async/await 从概念到底层机制讲透:异步到底解决什么问…- 6
- 0
异步编程
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
我的优惠劵
-
¥优惠劵使用时效:无法使用使用时效:
之前
使用时效:永久有效优惠劵ID:×
没有优惠劵可用!
















