3

我不太擅长异步编程,所以问题可能是低级的。

我在 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 没有被阻止,数据在可用时会立即推送到屏幕上。

但是在 Web 应用程序上,我真的看不出有什么不同。我还创建了与同步相同的功能,它们几乎相同。

我想知道的是:

  1. 我在具有Intel Core 2 Duo CPU T5750 2.00GHz的机器上运行此应用程序。处理器数量会影响 C# 上异步线程的性能吗?
  2. 从 Web 应用程序的角度来看,我在这里做错了吗?
4

2 回答 2

3

当我们在桌面应用程序(例如 WPF 应用程序)上运行相同的功能时,我们可以看到明显的性能差异。UI 没有被阻止,数据在可用时会立即推送到屏幕上。

这是一个普遍的误解。您发布的方法比其同步等效方法稍慢,但在 UI 应用程序中它的响应速度更快,因此看起来性能更高。

在 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 来说,完成该方法的时间并没有因为.GetMoviesMasync

并行组合(使用Task.WhenAllor Task.WhenAnyGetMoviesM作为一个整体可以更快地运行,因为它并行处理请求。此外,您还可以获得上述线程的好处。

在这种情况下,处理器的数量无关紧要。它们仅在您在线程池(例如Task.Run)上进行处理时发挥作用,而不是在进行 I/O 时发挥作用。(这是一个简化,但足够真实)。

于 2011-12-07T01:54:41.307 回答
0

几点评论。

首先,WebClient 默认情况下,每个服务器每个会话只打开 2 个连接。这显然会影响您的扩展能力,因此您可能想要更改它[请参阅如何以编程方式删除 WebClient 中的 2 个连接限制]

其次,我不确定在控制器方法和 QueryMoviesAsync 方法中使用异步有什么好处。

第三,WebClient 实现了 IDisposable,因此您应该将它与 using(..) 语句一起使用。不这样做也可能会影响可伸缩性。

鉴于上述所有更改,并回答您最初的问题,是的,代码应该在运行时扩展到多个处理器/内核,因为这是 ASP.NET/IIS 的默认设置

于 2011-12-06T06:33:39.233 回答