【问题标题】:C# asynchronous method - help neededC# 异步方法 - 需要帮助
【发布时间】:2013-07-11 05:27:36
【问题描述】:

我有一个使用 Web API 操作的异步方法。它似乎陷入了一个循环。我这样做的理由是,如果我在 catch 块的第 1 行设置一个断点并进入,它实际上永远不会到达第二行。

我最初返回一个包含 100,000+ (~30mb) 行的数据集,并认为由于请求的大小可能会很慢,但是在将我的 Web API 操作更改为仅返回 1 行之后,问题仍然存在.当我浏览到 Web API 解决方案的 URI 时,肯定会返回数据,我的浏览器中返回了 JSON。

public async Task<IEnumerable<Document>> GetAll()
{
    try
    {
        var response = await _client.GetAsync(
                       string.Format("{0}/api/document", _baseURI));

        // never hits line below
        return await response.Content.ReadAsAsync<Document>() 
                   as IEnumerable<Document>;
    }
    catch (Exception ex)
    {
        // handle exception
    }
}

我不确定我是否在这里遗漏了什么?一些帮助将不胜感激。

编辑 1

在回答一些问题时,我有一个 MVC 项目引用的 Web API 项目。我不得不对 JSON 反序列化的原始问题进行一些更改。

存储库:

public async Task<IEnumerable<Document>> GetAll()
{
    try
    {
        string json;
        var response = await _client.GetAsync(string.Format(
                       "{0}/api/document", _baseURI)).ConfigureAwait(false);

        var resource = await response.Content.ReadAsAsync<Document>();

        using(var reader = new StreamReader(resource.ToString()))
        {
            json = reader.ReadToEndAsync().ToString();
        }

        return JsonConvert.DeserializeObjectAsync<Document>(json) 
                       as IEnumerable<Document>;
        }
        catch (Exception)
        {
            throw;
        }
    }

控制器:

public async Task<ActionResult>  GetAll()
{
    return PartialView("_GetAllDocumentsPartial", await _docRepo.GetAll());
}

通过以下答案中描述的更改,在进行上述调试时仍然会出现相同的问题。但是我得到“任务被取消”。存储库中方法中的 catch 异常。

Stack trace.

【问题讨论】:

  • 从不throw exstackoverflow.com/a/2999314/34397
  • 你确定它永远不会打到第二行吗?异步和调试存在问题...您也可以尝试在第二行设置断点以确保。
  • 是的,只是在此处放置了一个断点,但并没有击中它。 Chrome 只是继续加载/页面是空白的。
  • 为了进一步巩固您对示例前面最后一句的断言,您是否尝试过使用 fiddler 来确保数据正在返回到您的实际应用程序?也就是说,假设你在一台机器上运行它,你可以在两者之间找到提琴手......
  • Fiddler 中没有返回 JSON。 GET 从未真正完成。尽管浏览到 URI 会直接返回 JSON,但该服务绝对可用。

标签: c# asp.net-mvc asynchronous asp.net-web-api async-await


【解决方案1】:

您是从 ASP.NET 应用程序调用 GetAll().Result 吗?既然您已将 MVC 标记为这个问题,我假设是这样。如果你这样做了,那么你自己就会陷入僵局。

假设你像这样调用 Web API。

public ActionResult Index()
{
    var result = GetAll().Result;

    return View();
}

这里的问题是当异步调用完成时,它需要在它跳出的原始线程上继续,但你通过调用Result() 阻塞了该线程。调用结果阻塞并等待异步调用返回,异步继续等待 ASP.NET 线程可用以继续。那是死锁。

像这样改变你的 GetAll()。

public async Task<IEnumerable<Document>> GetAll()
{
    try
    {
        var response = await _client.GetAsync(
                       string.Format("{0}/api/document", _baseURI))
                           .ConfigureAwait(false);

        // never hits line below
        return await response.Content.ReadAsAsync<Document>() 
                   as IEnumerable<Document>;
    }
    catch (Exception ex)
    {
        // handle exception
    }
}

这表明当异步调用完成时不需要保留上下文,因此继续发生在线程池线程(不是 ASP.NET)中。

最好将操作方法​​更改为public async Task&lt;ActionResult&gt; Index() 并等待GetAll()

如果你觉得这个答案有帮助,你只能感谢Stephen Cleary

【讨论】:

  • 抱歉延迟回复。我从来没有像你在回答中描述的那样做过,即var result = GetAll().Result;。请查看更新后的问题。
  • 当我添加.ConfigureAwait(false) 并对其进行调试时,它会进入下一行,但是,在适当的位置响应不包含content 的定义,即我不能去respsonse.content...。使用.ConfigureAwait(false)时如何访问响应的Result
  • 类似var x = await response.GetAwaiter().GetResult()... 的作品。其他一些小问题阻止我到达我想去的地方,也许是另一个问题。
【解决方案2】:

您是否在外部库中工作?如果是这样,请尝试添加 ConfigureAwait(false);

var response = await _client.GetAsync(string.Format("{0}/api/document", _baseURI))
                            .ConfigureAwait(false);

它与告诉 await 不要捕获当前上下文有关。调用 ConfigureAwait 将确保使用 ThreadPool 执行该方法的其余部分。可以在 Stephen Cleary 的博客here 上找到有关该主题的一些好读物。

【讨论】:

  • 是的,我有一个使用 Web API 项目的 MVC 项目。我已根据您的回答进行了更改。请查看更新后的问题。
【解决方案3】:

如果它适用于网络浏览器但不使用代码,那么您可能需要发送一个接受标头。添加以下行

_client.DefaultRequestHeaders.Accept = new MediaTypeWithQualityHeaderValue("application/json");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-08
    • 2012-08-26
    • 1970-01-01
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    相关资源
    最近更新 更多