4

我仍在了解 .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返回 a 的方法TaskWebClient版本返回Task<byte[]>,而版本HttpClient返回Task<HttpResponseMessage>,我必须从中挖掘字节,但我正在调用.Result任何一种方式,我希望在离开函数之前完成。

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

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

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

4

2 回答 2

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,以及http://www.tugberkugurlu.com/ archive/asynchronousnet-client-libraries-for-your-http-api-and-awareness-of-async-await-s-bad-effects用于解释 async/await 模式的死锁效应。

于 2013-05-24T10:29:32.657 回答
1

我不是 100% 确定,但这可能是由于不正确使用异步方法造成的。也许您看到这种行为是因为您不希望通过调用 Result 以同步方式使用 Async 方法。

于 2013-04-01T20:04:34.063 回答