0

我发现 .NET Framework 的 HttpClient 类/对象和 VS-2013 Project PhotoServer (DLL) 类/对象之间存在有趣的差异。这让我想知道脚本是否存在错误。

我正在使用 .NET Framework v4.5.1。

我在同步 Web 通用处理程序中使用 HttpClient 脚本。注意到我正在使用异步 POST 的“.Result”来等待响应。所以,看看 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)。同样,我正在使用“.Result”,但它不起作用,它只是死锁并挂起。我想知道的是为什么会这样,脚本是否有错误?

 //[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;
    }
4

2 回答 2

2

在这两种情况下,像这样使用.Result实际上都是一个错误;它只是碰巧没有在这种HttpClient情况下陷入僵局。请注意,如果像这样使用HttpClient其他平台(尤其是 Windows Phone、IIRC)上的相同库,将会死锁。

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

每次使用await. 当async方法恢复时,它将在该上下文中恢复。但是,诸如此类的类型HttpContext不是多线程安全的,因此 ASP.NET 将该上下文一次限制为一个线程。所以如果你通过调用.Result来阻塞一个线程,它就会阻塞那个上下文中的一个线程。

GetPhotoUrlsAsync死锁的原因是因为它是一个async试图在该上下文中恢复的方法,但在该上下文中已经有一个线程被阻塞。之所以HttpClient起作用,是因为GetAsyncetc. 实际上不是async方法(请注意,这是一个实现细节,您不应依赖此行为)。

解决此问题的最佳方法是替换.Resultawait

var managerVehiclePhoto = new ManagerVehiclePhoto();
var photoUrls = await managerVehiclePhoto.GetPhotoUrlsAsync("12345678901234567");
于 2014-07-16T16:11:19.153 回答
1

我又来了(回复:同样的任务 WebPages async):) 也就是说,我是 Stephen Cleary 认为“尝试迁移到异步”的人之一,所以这一切(仍然)是一个学习时刻。

问题是GUI/ASP.Net 中的SynchronizationContext。我不会破坏斯蒂芬的解释,所以链接是你最好的选择。

鉴于那篇文章中的最佳实践,这是我的方式(因此我在网页中使用的“顶级调用”)来“模拟”调用就像你正在做的那样awaitPostAsync在这种情况下,我正在使用ConfigureAwait(调用来自 WebPages,而不是 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 社区对此发表评论和/或纠正/改进:

“为什么我不必ConfigureAwait打开ReadAsStringAsync”(无论哪种方式都有效)?

  • 因为在这一点上,它是“一路异步”。我本可以等待其他一些异步方法......

...所以学习时刻继续:)

于 2014-07-16T16:11:06.757 回答