WebClient.DownloadDataTaskAsync与HttpClient.GetAsync的区别

5

我仍在了解.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 都是返回 Taskasync 方法。其中,WebClient 版本返回 Task<byte[]>,而 HttpClient 版本返回 Task<HttpResponseMessage>,我需要从中挖掘出字节,但无论哪种方式,我都调用了 .Result,我希望在离开函数之前完成。

对于(1),我会得到一个带有 An asynchronous operation cannot be started at this time... 的黄屏错误。对于(2),一切正常。

我可以改变整个堆栈,并在控制器方法自身和 SomeExpensiveFunction 上使用 async,然后一切正常。但我试图弄清楚是否存在与(1)或使用 MVC 时与 WebClient 总体上存在一些根本性问题。您有什么想法吗?

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

2个回答

2

您遇到了ASP.NET的同步上下文问题。为了使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了解异步控制器的概述,以及http://www.tugberkugurlu.com/archive/asynchronousnet-client-libraries-for-your-http-api-and-awareness-of-async-await-s-bad-effects解释异步/等待模式的死锁效应。

1
我不是100%确定,但这可能是由于不正确使用异步方法所致。也许您看到这种行为是因为不应该通过调用Result以同步方式使用异步方法。

我发现我在那里做错了什么,但是我使用 HttpClient 做完全相同的事情却没有错误。也许 WebClientHttpClient 在内部异步处理方面有所不同,即使最终它们都返回一个 Task<> - Joe Enos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接