【问题标题】:.NET HttpClient hangs when using await keyword?.NET HttpClient 在使用 await 关键字时挂起?
【发布时间】:2023-03-13 01:02:02
【问题描述】:

在考虑了这个有趣的答案HttpClient.GetAsync(...) never returns... 之后,我仍然遇到使用 await 时我的 HttpClient 没有返回的情况(下面的示例代码)。此外。我在 asp.net MVC5 控制器(UI 驱动)和 WebApi 中都使用了这个帮助程序。我能做什么:

  1. 使用 await 代替(可怕的) .Result 并且仍然有这个函数返回?
  2. 在 MVC 控制器和 WebApi 中重复使用相同的例程?

显然,我应该将 .Result 替换为 .ConfigureAwait(false) 但这似乎与上面引用的帖子中的“Task4”与等待 httpClient.GetAsync 一起工作的事实相矛盾。还是我需要单独的控制器和 WebApi 例程?

public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
    if (string.IsNullOrEmpty(elasticQuery)) return null;

    IEnumerable<TcMarketUserFullV1> res = null;
    using (var hclient = new HttpClient())
    {
        hclient.BaseAddress = new Uri("https://addr.servicex.com");
        hclient.DefaultRequestHeaders.Accept.Clear();
        hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
            CloudConfigurationManager.GetSetting("jwt-bearer-token"));

        // Why does this never return when await is used?
        HttpResponseMessage response =  hclient.GetAsync("api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result; 
        if (response.IsSuccessStatusCode)
        {
            var content = await response.Content.ReadAsStringAsync();
            res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
        }
        else{log.Warn("...");}
    } 
    return res;
} 

更新:我的调用链,从 Telerik Kendo Mvc.Grid DataBinding 调用开始如下:

[HttpPost]
public async Task<ActionResult> TopLicenseGrid_Read([DataSourceRequest]DataSourceRequest request)
{
    var res = await GetLicenseInfo();
    return Json(res.ToDataSourceResult(request)); // Kendo.Mvc.Extensions.DataSourceRequest
}

然后:

private async Task<IEnumerable<CsoPortalLicenseInfoModel>> GetLicenseInfo()
{
  ...
  // Never returns
  var qry = @"app_metadata.tc_app_user.country:""DE""";
  return await TcSearchMultiUsersAsync(qry);
}

然后,上面完整显示的例程,但现在没有 .Result:

public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
    if (string.IsNullOrEmpty(elasticQuery)) return null;

    IEnumerable<TcMarketUserFullV1> res = null;
    using (var hclient = new HttpClient())
    {
        hclient.BaseAddress = new Uri("https://addr.servicex.com");
        hclient.DefaultRequestHeaders.Accept.Clear();
        hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
            CloudConfigurationManager.GetSetting("jwt-bearer-token"));

        // This now returns fine
        HttpResponseMessage response =  hclient.GetAsync("api/v2/users?search_engine=v2&q=" + elasticQuery"); 
        if (response.IsSuccessStatusCode)
        {
            // This returns my results fine too
            var content = await response.Content.ReadAsStringAsync();
            // The following line never returns results. When debugging, everything flows perfectly until I reach the following line, which never 
            // returns and the debugger returns me immediately to the top level HttpPost with a result of null. 
            res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
        }
        else{log.Warn("...");}
    } 
    return res;
} 

【问题讨论】:

  • 您在hclient.SendAsync 中缺少await
  • 我知道。这是因为当我将 await 放在那里时它“挂起”。请注意代码行上方的注释。它将向我提供返回数据的唯一方法是当我使用(可怕的).Result 时。那是我不明白的......

标签: c# asynchronous asp.net-mvc-5 async-await task-parallel-library


【解决方案1】:

你需要await 一切。你在其中一个上await 是不够的,你需要在所有这些上await

这个:

HttpResponseMessage response = hclient.GetAsync(
                        "api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;

应该是:

HttpResponseMessage response = await hclient.GetAsync(
                                "api/v2/users?q=" + elasticQuery + "&search_engine=v2");

为了死锁,对.Result 进行一次阻塞调用就足够了。你需要"async all the way"

【讨论】:

  • 谢谢。但这正是我遇到的问题,而不是答案。你看,当我删除 .Result 并使用 await 时,它会挂起线程,当我使用 .Result 时,我至少会从 httpClient 调用中得到结果。
  • @GGleGrand 这听起来很奇怪。显示TcSearchMultiUsersAsync的整个调用链
  • 顶级操作仍然是 ActionResult TopGrid_Read。更改为公共异步 Task TopGrid_Read 似乎已解决。谢谢!
  • 我说得太早了。它曾经奏效似乎是侥幸。我会尝试将我的通话流程和观察结果添加到原始问题中。
  • 可能只是时间问题——即运气 = 定时炸弹——我们会看到......测试继续。
猜你喜欢
  • 2013-10-02
  • 2013-05-26
  • 2023-03-24
  • 2018-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-20
  • 1970-01-01
相关资源
最近更新 更多