我不太擅长异步编程,所以问题可能处于低级别。
我在ASP.NET MVC 4 Dev上使用Async CTP创建了一个异步方法。预览:
public class Movie
{
public string Title { get; set; }
public string Url { get; set; }
public string BoxArtUrl { get; set; }
}
public class MovieM {
public IEnumerable<Movie> M2009 { get; set; }
public IEnumerable<Movie> M2010 { get; set; }
public IEnumerable<Movie> M2011 { get; set; }
}
public class HomeController : AsyncController {
public async Task<ActionResult> GetMoviesM() {
var profiler = MiniProfiler.Current; // it's ok if this is null
var pageSize = 1000;
var imageCount = 0;
using (profiler.Step("Start pulling data (Async) and return it")) {
var m2009 = await QueryMoviesAsync(2009, imageCount, pageSize);
var m2010 = await QueryMoviesAsync(2010, imageCount, pageSize);
var m2011 = await QueryMoviesAsync(2011, imageCount, pageSize);
return View(new MovieM {
M2009 = m2009,
M2010 = m2010,
M2011 = m2011
});
}
}
XNamespace xa = "http://www.w3.org/2005/Atom";
XNamespace xd = "http://schemas.microsoft.com/ado/2007/08/dataservices";
XNamespace xm = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
string query = "http://odata.netflix.com/Catalog/Titles?$filter=ReleaseYear eq {0}&$skip={1}&$top={2}&$select=Url,BoxArt";
async Task<IEnumerable<Movie>> QueryMoviesAsync(int year, int first, int count) {
var client = new WebClient();
var url = String.Format(query, year, first, count);
var data = await client.DownloadStringTaskAsync(new Uri(url));
var movies =
from entry in XDocument.Parse(data).Descendants(xa + "entry")
let properties = entry.Element(xm + "properties")
select new Movie
{
Title = (string)entry.Element(xa + "title"),
Url = (string)properties.Element(xd + "Url"),
BoxArtUrl = (string)properties.Element(xd + "BoxArt").Element(xd + "LargeUrl")
};
return movies.AsEnumerable();
}
}
代码工作得很好。当我们在桌面应用程序(例如WPF应用程序)上运行相同的功能时,我们可以看到明显的性能差异。 UI未被阻止,数据在可用时立即被推送到屏幕。
但是在网络应用程序中,我真的看不出有什么区别。我还创建了与sync相同的功能,它们两者几乎相同。
我想知道的是:
答案 0 :(得分:3)
当我们在桌面应用程序(例如WPF应用程序)上运行相同的功能时,我们可以看到明显的性能差异。 UI未被阻止,数据在可用时立即被推送到屏幕。
这是一种常见的误解。您发布的方法比其同步等效的慢,但在UI应用中,它更像响应,因此显示更高效。< / p>
在ASP.NET场景中,只有在所有异步请求完成后才会呈现页面;这就是为什么你没有看到差异。
通过并行化请求,您可以使实际更高效:
var m2009Task = QueryMoviesAsync(2009, imageCount, pageSize);
var m2010Task = QueryMoviesAsync(2010, imageCount, pageSize);
var m2011Task = QueryMoviesAsync(2011, imageCount, pageSize);
await Task.WhenAll(m2009Task, m2010Task, m2011Task);
var m2009 = await m2009Task;
var m2010 = await m2010Task;
var m2011 = await m2011Task;
这将提高桌面和ASP.NET的性能。
您的原始代码使用序列组合(一次await
个)。在这种情况下,代码异步运行,但请求没有更快地完成。使用这样的async
/ await
仍然有一个好处:请求不会占用ASP.NET线程,因此您的服务可以扩展更多。在UI世界中,UI线程不会被束缚,因此它的响应速度更快。但对于ASP.NET和UI,完成 {/ 1}}方法的总时间不会因为GetMoviesM
而减少。
并行组合(使用async
或Task.WhenAll
)允许Task.WhenAny
整体运行得更快,因为它并行执行请求。此外,您还可以获得上述线程优势。
在这种情况下,处理器的数量无关紧要。它们只在您对线程池进行处理时才会发挥作用(例如GetMoviesM
),而不是在进行I / O时。 (这是一种简化,但足够真实)。
答案 1 :(得分:0)
一些评论。
首先,WebClient默认情况下每个会话每个服务器只打开2个连接。这显然会影响您的缩放能力,因此您可能希望更改[见How can I programmatically remove the 2 connection limit in WebClient]
其次,我不确定在控制器方法和QueryMoviesAsync方法中使用async有什么好处。
第三,WebClient实现了IDisposable,因此您应该使用using(..)语句。不这样做也可能影响可扩展性。
鉴于上述所有更改,并回答您的原始问题,是的,代码应该在运行时扩展到多个处理器/核心,因为这是ASP.NET / IIS的默认值