异步任务适用于 HttpClient 对象,但不适用于 Web 泛型处理程序中的项目类



我发现。net框架的HttpClient类/对象和VS-2013 Project PhotoServer (DLL)类/对象之间有一个有趣的差异。这让我怀疑脚本是否有错误。

我使用的是。net Framework v4.5.1。

我在同步Web通用处理程序中使用HttpClient脚本。注意到我用了"。Result",异步POST等待响应。所以,看看HttpClient的工作方式是

 using (var httpClient = new HttpClient())
 {
     var response = httpClient.PostAsync(
         _baseUrl,
         new FormUrlEncodedContent
         (
             new List<KeyValuePair<string, string>>
             {
                 new KeyValuePair<string, string>("Vin", parmVin), 
                 new KeyValuePair<string, string>("ImageSize", parmImageSize)
             }.ToArray()
         )
     ).Result;
     //returned string[] datatype...
     var photoUrls = response.Content.ReadAsStringAsync().Result;
 }

我在同步Web通用处理程序中使用"GetPhotoUrlsAsync"脚本。这个"GetPhotoUrlsAsync"对象来自Project类(DLL)。同样,我用的是"。结果",它不起作用,它只是陷入僵局和挂起。我想知道的是为什么会这样,剧本有问题吗?

 //[Scripts in Web Generic Handlers]...
 var managerVehiclePhoto = new ManagerVehiclePhoto();
 var photoUrls = managerVehiclePhoto.GetPhotoUrlsAsync("12345678901234567").Result;
 //[Project Class]...
 namespace BIO.Dealer.Integration.PhotoServer
 {
      public seal class VehiclePhotoManager
      {
          public async Task<string[]> GetPhotoUrlsAsync(string vin)
          {
              var listResponse = await _client.ListAsync(vin);
              return listResponse.ToArray();
          }
      }
 }

谢谢…

编辑# 1

    //Synchronous API Call...
    public string[] GetPhotoUrls(string vin)
    {
        return GetPhotoUrlsAsync(vin).Result;
    }

这样使用.Result实际上在这两种情况下都是一个bug;在HttpClient的情况下,它恰好没有死锁。注意,相同的HttpClient库在其他平台上(特别是Windows Phone, IIRC) 死锁,如果这样使用。

我在我的博客中详细描述了死锁,但它的要点是:

有一个ASP。每次使用await时默认捕获的. NET"请求上下文"。当async方法恢复时,它将在该上下文中恢复。然而,像HttpContext这样的类型并不是多线程安全的。NET将该上下文一次限制为一个线程。因此,如果你通过调用.Result来阻塞一个线程,它就阻塞了该上下文中的一个线程。

GetPhotoUrlsAsync死锁的原因是因为它是async方法,试图在该上下文中恢复,但是在该上下文中已经有一个线程阻塞。HttpClient碰巧工作的原因是因为GetAsync等实际上不是async方法(注意这是一个实现细节,你不应该依赖于这个行为)。

最好的解决方法是用await替换.Result:

var managerVehiclePhoto = new ManagerVehiclePhoto();
var photoUrls = await managerVehiclePhoto.GetPhotoUrlsAsync("12345678901234567");

Me again (re: same quest WebPages async):)也就是说,我是Stephen Cleary认为"试图迁移到async"的人之一,所以这一切(仍然)是一个学习的时刻。

问题是GUI/ASP.Net中的SynchronizationContext。我不会弄乱Stephen的解释,所以这个链接是你最好的选择。

考虑到那篇文章中的最佳实践,下面是我的方法(因此我在WebPages中使用"顶级调用")来"模拟"awaitPostAsync调用,就像您正在做的那样。在这种情况下,我使用ConfigureAwait(调用来自网页,而不是MVC):

public static async Task<string> PostToRequestBin()
{       
    var _strContent = new FormUrlEncodedContent(new[] {new KeyValuePair<string,string>("fizz","buzz")});
    using (var client = new HttpClient())
    {
        /*
         * See http://requestb.in/ for usage             
         */
        var result = await client.PostAsync("http://requestb.in/xyzblah", _strContent).ConfigureAwait(false);
        return await result.Content.ReadAsStringAsync();
    }
}

在我的WebPages页面:

@{
   //Class2 is a mock "library" in App_Code where the above async code lives
   var postcatcher = Class2.PostToRequestBin(); 
}

并在页面的某个地方使用它(我使用Task<string>.Result:

)
<p>@postcatcher.Result</p>
再一次,这是一个学习的时刻,我希望它能帮助/指导你。我完全期望SO社区对此发表评论和/或纠正/改进:

"为什么我不必在ReadAsStringAsyncConfigureAwait "(无论哪种方式都有效)?

  • ,因为在这一点上,它是"异步的所有方式"。我可以等待其他异步方法…

…所以学习时刻还在继续:)

相关内容

最新更新