WebClient.DownloadDataTaskAsync与HttpClient.GetAsync

时间:2013-01-14 22:21:27

标签: c# asp.net asp.net-mvc async-await

我仍然了解.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.DownloadDataTaskAsyncHttpClient.GetAsync都是async方法,它们返回TaskWebClient版本返回Task<byte[]>,而HttpClient版本返回Task<HttpResponseMessage>,我必须从中挖出字节,但我以任何方式调用.Result,我希望在离开这个功能之前完成。

使用(1),我得到一个带有An asynchronous operation cannot be started at this time...的黄色屏幕。随着(2),一切正常。

我可以更改整个堆栈并在控制器方法本身和async上使用SomeExpensiveFunction,一切正常。但我正在试图弄清楚在使用MVC时,(1)或WebClient是否存在根本性的错误。有什么想法吗?

编辑:我知道在这个例子中我可以使用这些调用的同步版本,因为我实际上并没有异步执行任何操作 - 这只是一个基于更大代码库的示例。

2 个答案:

答案 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