C# 调用EF异步方法时,异步任务冻结
我有一个返回xml字符串的异步方法。当我将任务添加到任务列表中时,它会启动任务,但在使用实体框架与数据库对话时会挂起等待。下面是示例代码C# 调用EF异步方法时,异步任务冻结,c#,entity-framework,asynchronous,C#,Entity Framework,Asynchronous,我有一个返回xml字符串的异步方法。当我将任务添加到任务列表中时,它会启动任务,但在使用实体框架与数据库对话时会挂起等待。下面是示例代码 public async Task<ActionResult> GenerateXml(long id) { var tasks = new List<Task<string>>(); tasks.Add(GenerateXmlAsync(id)); Task.WaitAll(tasks.ToArra
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;
}
}
公共异步任务GenerateXml(长id)
{
var tasks=新列表();
添加(GenerateXmlAsync(id));
Task.WaitAll(tasks.ToArray());
}
专用异步任务GenerateXmlAsync(长id)
{
使用(var dbContext=new MyDatabaseContext())
{
var item=await dbContext.Items.FirstOrDefaultAsync(itm=>itm.Id=Id);
/*做一些计算,生成xml*/
var xml=“”;
返回xml;
}
}
使用Azure上的流日志,我可以看到任务正在运行,但从未通过dbContext异步方法。它会挂起有什么原因吗?如果您的代码在异步代码上阻塞,它将被挂起。我在我的博客上详细描述了这一点,但总的要点是:
- 当
生成调用线程时,默认情况下它首先捕获一个“上下文”。这通常是UI上下文(对于UI应用程序)、ASP.NET请求上下文(对于服务器应用程序)或线程池上下文wait
- 当等待操作完成时,
状态机通过将自身调度到该上下文来恢复执行。因此,UI线程上的async
方法将在该UI线程上恢复,处理ASP.NET请求的async
方法将恢复处理相同的ASP.NET请求async
- 某些上下文(例如UI上下文和ASP.NET请求上下文)一次只允许一个线程进入。因此,如果在该上下文中有一个线程被阻塞,那么
方法将在恢复之前等待该线程。在这种情况下,线程在等待async
方法完成时被阻塞,但由于它正在等待被阻塞的线程,因此无法执行此操作。经典的死锁async
你也会发现我的帖子很有帮助。最后,我列出了“旧的阻塞方式”(应该避免)以及“新的异步方式”。本例中的相关示例是将
Task.WaitAll
替换为wait Task。当出现所有的连接问题时。。。您可以调试它吗?如果我将FirstOrDefaultAsync
更改为FirstOrDefault
并删除wait
,它工作正常。避免死锁的强制参考:任务。WaitAll
是一个阻塞调用。您正在积极阻止呼叫。事实上,GenerateXml
没有await
s,这意味着它将同步执行,编译器将给您一个关于it@PanagiotisKanavos这是正确的。您正在使用Task.WaitAll
创建死锁。UI线程正在等待GenerateXmlAsync
完成GenerateXmlAsync
正在等待UI线程返回消息泵,以便它可以继续工作。也就是说,使用WaitAll
阻止了等待I/O操作时打开的线程,该线程必须在另一个线程上启动。使用wait Task.whalll
修复了该问题。谢谢