我仍然了解.NET 4.5的各种异步功能,而且我遇到了一些有趣的东西。鉴于我的MVC控制器中的以下内容,我在执行(1)和(2)时获得了不同的结果
public ActionResult Index() {
var stuff = SomeExpensiveFunction();
return View(stuff);
}
private byte[] SomeExpensiveFunction() {
string url = "http://some-url.../";
// (1)
var wc = new WebClient();
return wc.DownloadDataTaskAsync(url).Result;
// (2)
var hc = new HttpClient();
return hc.GetAsync(url).Result.Content.ReadAsByteArrayAsync().Result;
}
从表面上看,它们看起来是一样的 - WebClient.DownloadDataTaskAsync
和HttpClient.GetAsync
都是async
方法,它们返回Task
。 WebClient
版本返回Task<byte[]>
,而HttpClient
版本返回Task<HttpResponseMessage>
,我必须从中挖出字节,但我以任何方式调用.Result
,我希望在离开这个功能之前完成。
使用(1),我得到一个带有An asynchronous operation cannot be started at this time...
的黄色屏幕。随着(2),一切正常。
我可以更改整个堆栈并在控制器方法本身和async
上使用SomeExpensiveFunction
,一切正常。但我正在试图弄清楚在使用MVC时,(1)或WebClient
是否存在根本性的错误。有什么想法吗?
编辑:我知道在这个例子中我可以使用这些调用的同步版本,因为我实际上并没有异步执行任何操作 - 这只是一个基于更大代码库的示例。
答案 0 :(得分:1)
我不是百分百肯定,但这可能是由于不正确使用Async方法造成的。也许您看到了这种行为,因为您不希望通过调用Result以同步方式使用Async方法。
答案 1 :(得分:1)
你已经违反了ASP.NET的SynchronizationContext。要使WebClient示例正常工作,您应该使整个控制器异步。试试这个:
public async Task<ActionResult> IndexAsync() {
string url = "http://some-url.../";
using (var wc = new WebClient())
return View(await wc.DownloadDataTaskAsync(url));
}
有关异步控制器的简要信息,请参阅http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4;有关async / await模式的死锁效果的解释,请参阅http://www.tugberkugurlu.com/archive/asynchronousnet-client-libraries-for-your-http-api-and-awareness-of-async-await-s-bad-effects。