处理器数量会影响 ASP.NET MVC 4 Web 应用程序中异步线程的性能吗?
我不太擅长异步编程,因此问题可能处于较低水平。
我在 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 应用程序)上运行相同的功能时,我们可以看到明显的性能差异。用户界面不会被阻塞,数据可用时会立即推送到屏幕。
但在网络应用程序上,我真的看不出有什么区别。我还创建了与sync相同的函数,它们几乎是相同的。
我想知道的是:
- 我在具有 Intel Core 2 Duo CPU T5750 2.00GHz 的计算机上运行此应用程序。处理器数量会影响 C# 上异步线程的性能吗?
- 从网络应用程序的角度来看,我在这里做错了什么吗?
I am not so good at asynchronous programming so the question might be at the low level.
I have created an async method as below with Async CTP on ASP.NET MVC 4 Dev. Preview:
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();
}
}
The code works just fine. When we run the same function on a desktop app (a WPF App for instance), we can see a tangible performance difference. The UI isn't blocked, the data is being pushed to screen instantly when it is available.
But on a web application, I really cannot see a difference. I also created the same function as sync and the both of them are nearly the same.
What I would like to know is that:
- I run this app on a machine which has Intel Core 2 Duo CPU T5750 2.00GHz. Do number of processors effect the performance of asynchronous thread on C#?
- Am I doing something wrong here from a web application point of view?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一个常见的误解。您发布的方法比其同步等效方法稍微慢,但在 UI 应用程序中,它响应更快,因此看起来性能更高。
在 ASP.NET 场景中,仅当所有异步请求完成时才会呈现页面;这就是为什么你看不到差异。
您可以通过并行化您的请求来使其实际上性能更高:
这将提高桌面和 ASP.NET 的性能。
您的原始代码使用串行组合(一次一个
await
)。在这种情况下,代码异步运行,但请求的完成速度并没有更快。像这样使用async
/await
仍然有一个好处:请求不会占用 ASP.NET 线程,因此您的服务可以进一步扩展。在 UI 世界中,UI 线程不会受到束缚,因此响应速度更快。但对于 ASP.NET 和 UI,完成GetMoviesM
方法的总时间并不会通过使其异步.
并行组合(使用
Task.WhenAll
或Task.WhenAny
)允许GetMoviesM
作为一个整体运行得更快,因为它并行执行请求。此外,您还可以获得上面提到的线程优势。在这种情况下,处理器的数量并不重要。它们仅在您在线程池上进行处理时发挥作用(例如,Task.Run),而不是在进行 I/O 时发挥作用。 (这是一种简化,但足够真实)。
This is a common misconception. The method you posted is slightly slower than its synchronous equivalent, but in a UI app it is more responsive, and therefore appears more performant.
In an ASP.NET scenario, the page is only rendered when all asynchronous requests have completed; that is why you don't see a difference.
You can make it actually more performant by parallelizing your requests:
This will improve the performance for both desktop and ASP.NET.
Your original code uses serial composition (one
await
at a time). In this case, the code is running asynchronously, but the request is not completed any faster. There is still a benefit to usingasync
/await
like this: the request does not tie up an ASP.NET thread, so your service can scale up more. In the UI world, the UI thread does not get tied up, so it's more responsive. But for both ASP.NET and UI, the total time to complete theGetMoviesM
method is not reduced by making itasync
.Parallel composition (using
Task.WhenAll
orTask.WhenAny
) allowsGetMoviesM
as a whole to run faster, since it does the requests in parallel. In addition, you get the thread benefits mentioned above.In this case, the number of processors does not matter. They only come into play when you're doing processing on the thread pool (e.g.,
Task.Run
), not when doing I/O. (This is a simplification, but true enough).一些评论。
首先,WebClient 默认情况下每个会话每个服务器仅打开 2 个连接。这显然会影响您的扩展能力,因此您可能需要更改它[请参阅 如何以编程方式删除 WebClient 中的 2 个连接限制 ]
其次,我不确定在控制器方法和 QueryMoviesAsync 内部使用异步有什么好处 方法。
第三,WebClient 实现了 IDisposable,因此您应该通过 using(..) 语句来使用它。不这样做也可能会影响可扩展性。
鉴于上述所有更改,并回答您原来的问题,是的,代码应该在运行时扩展到多个处理器/内核,因为这是 ASP.NET/IIS 的默认设置
A few comments.
Firstly, WebClient by default only opens 2 connections per server, per session. This will obviously impact your scaling ability, so you might want to change that [see How can I programmatically remove the 2 connection limit in WebClient ]
Secondly, I'm not sure there's any benefit to using async both in your controller method AND inside the QueryMoviesAsync method.
Thirdly, WebClient implements IDisposable, so you should be using it with a using(..) statement. Not doing so may affect scalability as well.
Given all the above changes, and to answer your original question, yes the code should scale out over multiple processors/cores at runtime as that is the default for ASP.NET/IIS