同步方法调用async方法引发的ASP.NET应用程序崩溃
https://blog.csdn.net/weixin_34208283/article/details/93603295
摘自上面的博客:
在ASP.NET中(ASP.NET天生是多线程的,基于线程池的,没有UI线程的概念),如果你调用了一个async方法,如果有await相伴,当前线程立马被释放回线程池,线程的上下文信息(比如reqeust context)被保存;如果没有await相伴(也没有其他的wait代码),调用async方法之后,代码会继续往下执行,直至完成,当前线程被释放回线程池,线程的上下文信息不会被保存。当async中的异步任务完成后(注:异步任务不是在另外一个线程中完成的,是在一个状态机中完成的),会从线程池中取出一个线程继续执行,执行时会读取当时调用它的原线程的上下文信息(默认情况下的行为,如果ConfigureAwait(false) ,就没有这一步操作),如果当初调用时没有使用await,线程的上下文信息没有被保存,这时就会引发NullReferenceException。而在这种级别发生的未处理null引用异常,会引发整个应用程序崩溃,更准确地说是应用程序所在的进程崩溃。
在ASP.NET中要么老老实实地await;要么告诉async方法,不要读取原线程的上下文信息(ConfigureAwait(false),未经实际验证是否有效);要么调用async方法的线程没有需要保存的上下文信息,比如在Task.Run(或Task.Factory.StartNew)中调用async方法,也就是用一个新的线程调用async方法。
我来测试下ConfigureAwait(false)是否可以防止呢?
ConfigureAwait(false)
public string getPATData([FromUri]PATQuery patQuery)
{
try
{
Logs.Info($"主线程调用异步之前ID{Thread.CurrentThread.ManagedThreadId}执行");
var data = new
{
list = patService.GetPAT(patQuery).Result,
total = patQuery.records,
};
Logs.Info($"异步后的线程ID{Thread.CurrentThread.ManagedThreadId}同步上下文:{SynchronizationContext.Current?.ToString()}开始执行");
return data.ToJson();
}
catch (Exception e)
{
Logs.LogWriter(e.GetOriginalException());
}
return string.Empty;
}
PATService.cs
public class PATService
{
public async Task<List<PATDto>> GetPAT(PATQuery patQuery)
{
try
{
using (MESDB db = new MESDB())
{
var query = db.PATDto;
var result = await query.ToListAsync().ConfigureAwait(false);
return result;
}
}
catch (Exception ex)
{
}
return null;
}
}
输出日志如下:
2021-04-25 15:10:53.1943 INFO 主线程调用异步之前ID5执行
2021-04-25 15:10:53.7092 INFO 异步后的线程ID5同步上下文:System.Web.LegacyAspNetSynchronizationContext开始执行
结论:
上面的写法 await .ConfigureAwait(false) 防止崩溃。