异步任务在调用EF异步方法时冻结
本文关键字:异步方法 冻结 EF 调用 任务 异步 | 更新日期: 2023-09-27 17:59:30
我有一个返回xml字符串的异步方法。当我将任务添加到任务列表中时,它会启动任务,但在使用实体框架与数据库对话时会挂起第一个等待。下面是示例代码。
public async Task<ActionResult> GenerateXml(long id)
{
var tasks = new List<Task<string>>();
tasks.Add(GenerateXmlAsync(id));
Task.WaitAll(tasks.ToArray());
}
private async Task<string> GenerateXmlAsync(long id)
{
using (var dbContext = new MyDatabaseContext())
{
var item = await dbContext.Items.FirstOrDefaultAsync(itm => itm.Id = id);
/* do some calculations, generate the xml... */
var xml = "<generated by code above>";
return xml;
}
}
使用Azure上的流日志,我可以看到任务运行,但从未通过dbContext异步方法。它会挂起来是有原因的吗?
如果您的代码阻塞异步代码,则会出现死锁情况。我在我的博客上详细描述了这一点,但总的要点是:
- 当
await
产生调用线程时,默认情况下它首先捕获一个"上下文"。这通常是UI上下文(用于UI应用程序)、ASP.NET请求上下文(用于服务器应用程序)或线程池上下文 - 当awaitable操作完成时,
async
状态机通过将自身调度到该上下文来恢复执行。因此,UI线程上的async
方法将在该UI线程上恢复,而处理ASP.NET请求的async
方法将恢复处理同一ASP.NET请求 - 某些上下文(例如,UI上下文和ASP.NET请求上下文)一次只允许一个线程进入。因此,如果在该上下文中有一个线程被阻塞,那么
async
方法将等待该线程后再继续。在这种情况下,线程被阻塞,等待async
方法完成,但由于它正在等待被阻塞的线程,因此无法完成。经典的死锁
你可能也会发现我的async
介绍文章很有帮助。最后,我列出了"旧的阻塞式做事方式"(应该避免)以及"新的异步做事方式"。这种情况下的相关示例是用await Task.WhenAll
替换Task.WaitAll
。