我发现。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中使用"顶级调用")来"模拟"await
和PostAsync
调用,就像您正在做的那样。在这种情况下,我使用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社区对此发表评论和/或纠正/改进:
"为什么我不必在ReadAsStringAsync
上ConfigureAwait
"(无论哪种方式都有效)?
- ,因为在这一点上,它是"异步的所有方式"。我可以等待其他异步方法…
…所以学习时刻还在继续:)