.NET Framework 4.8 ASP.NET中Async/Await线程阻塞问题排查求助
ASP.NET .NET Framework 4.8 异步死锁问题排查与临时解决
问题现象
在ASP.NET .NET Framework 4.8中遇到特殊死锁问题:通过反射加载类后调用同步方法,该方法内部使用.Wait()或.GetAwaiter().GetResult()调用异步方法;若异步方法中的await未添加.ConfigureAwait(false),会出现调用永不返回、线程阻塞的情况,原应用甚至会导致IIS挂起(一段时间后因应用池回收恢复)。注:复现示例中IIS仍能接受新请求,但调用本身会一直阻塞。
此问题仅出现在ASP.NET .NET Framework 4.8环境,相同代码在.NET Framework 4.8的Windows服务、.NET 8中运行正常(DLL为多目标框架)。
临时解决方案
- 将页面改为全异步模式,从上到下改造代码→不可行,属于遗留单体应用,改造成本极高
- 为所有异步调用添加
.ConfigureAwait(false)→不可行,大量代码由自研ORM工具自动生成,修改成本过高 - 将涉及方法改为同步实现,移除所有异步调用→已采用此方案,虽不满意但影响范围小,改动成本低
问题推测与困惑
查阅资料后推测,可能是ASP.NET自定义的System.Web.AspNetSynchronizationContext导致回调异常,但因程序挂起无法验证,对此深感困惑。
补充:调试需求与本质疑问
真正想搞清楚的是:
- 如何调试这类编译器生成状态机的场景,定位线程恢复失败的原因
- 此问题的本质原因是什么
复现代码
页面调用代码
protected void Page_Load(object sender, EventArgs e) { string Lstr_PathDLL = "full path to DLL"; Assembly Lobj_Assembly = Assembly.LoadFile(Lstr_PathDLL); IAwaitErrorNetFrameworkASPNET Lobj_Error = (IAwaitErrorNetFrameworkASPNET)Lobj_Assembly.CreateInstance("LoadedByReflection.AwaitErrorNetFrameworkASPNET"); Lobj_Error.Test(); }
组件类代码
using Dominio; using System.Threading.Tasks; namespace LoadedByReflection { public class AwaitErrorNetFrameworkASPNET : IAwaitErrorNetFrameworkASPNET { public void Test() { Teste2().GetAwaiter().GetResult(); } private async Task Teste2() { await Task.Delay(1000); } } }
后续反馈
正如@Charlieface和Srinivasan Jayakumar所指出,创建新线程调用代码可解决问题,但这并非最佳实践,而老旧遗留系统难以遵循最佳实践。目前问题已解决,但仍无法调试System.Web.AspNetSynchronizationContext,也不清楚为何死锁仅出现在ASP.NET而非Windows服务中,感谢各位帮助。
内容的提问来源于stack exchange,提问作者Ângelo Cossa
相关产品推荐
相关产品推荐

