【问题标题】:async Task Freezes When Calling EF Async Methods调用 EF 异步方法时异步任务冻结
【发布时间】:2015-09-11 16:50:56
【问题描述】:

我有一个返回 xml 字符串的异步方法。当我将任务添加到任务列表时,它会启动任务,但在使用 Entity Framework 与数据库对话时会挂起第一个等待。下面是示例代码。

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 异步方法。它会挂起有什么原因吗?

【问题讨论】:

  • 连接问题...能调试一下吗?
  • 如果我将FirstOrDefaultAsync 更改为FirstOrDefault 并删除await,它可以正常工作。
  • 强制参考以避免死锁:msdn.microsoft.com/en-us/magazine/jj991977.aspx
  • Task.WaitAll 是一个阻塞调用。您正在主动阻止呼叫。事实上,GenerateXml 有 no awaits 这意味着它将同步执行,编译器会给你一个错误
  • @PanagiotisKanavos 是正确的。您正在使用Task.WaitAll 创建死锁。 UI 线程正在等待GenerateXmlAsync 完成。 GenerateXmlAsync 正在等待 UI 线程返回到消息泵,以便它可以继续工作。

标签: c# entity-framework asynchronous


【解决方案1】:

如果您的代码阻塞异步代码,则为subject to a deadlock situation。我在我的博客上对此进行了完整的描述,但总体要点是:

  • 当await 产生调用线程时,默认情况下它首先捕获“上下文”。这通常是 UI 上下文(用于 UI 应用)、ASP.NET 请求上下文(用于服务器应用)或线程池上下文。
  • 当等待操作完成时,async 状态机通过将自身调度到该上下文来恢复执行。因此,UI 线程上的 async 方法将在该 UI 线程上恢复,而处理 ASP.NET 请求的async 方法将恢复处理相同的 ASP.NET 请求。
  • 某些上下文(例如,UI 上下文和 ASP.NET 请求上下文)一次只允许一个线程进入。因此,如果在该上下文中存在线程阻塞,则async 方法将在恢复之前等待该线程。在这种情况下,线程被阻塞等待async 方法完成,因为它正在等待被阻塞的线程,所以它不能这样做。经典的僵局。

您可能还会发现我的async intro 帖子很有帮助。最后,我列出了“旧的阻塞做事方式”(应该避免)以及“新的异步做事方式”。本例中的相关示例是将Task.WaitAll 替换为await Task.WhenAll。

【讨论】:

  • 就是这样,使用WaitAll 阻塞了在等待 I/O 操作时打开的线程,该操作必须在另一个线程上启动。使用await Task.WhenAll 解决了这个问题。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 2015-06-07
  • 1970-01-01
相关资源
最近更新 更多