Il numero di processori influenza le prestazioni dei thread asincroni in un'applicazione Web ASP.NET MVC 4?

StackOverflow https://stackoverflow.com/questions/8390034

Domanda

Non sono così bravo nella programmazione asincrona, quindi la domanda potrebbe essere a basso livello.

Ho creato un metodo Async come di seguito con Async CTP su ASP.NET MVC 4 Dev. Anteprima:

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();
    }
}

Il codice funziona bene. Quando eseguiamo la stessa funzione su un'app desktop (ad esempio un'app WPF), possiamo vedere una differenza di prestazioni tangibili. L'interfaccia utente non è bloccata, i dati vengono spinti sullo schermo immediatamente quando è disponibile.

Ma su un'applicazione web, non riesco davvero a vedere una differenza. Ho anche creato la stessa funzione della sincronizzazione e entrambi sono quasi gli stessi.

Quello che vorrei sapere è che:

  1. Eseguo questa app su una macchina che ha Intel Core 2 Duo CPU T5750 2.00GHz. Il numero di processori influisce sulle prestazioni del thread asincrono su C#?
  2. Sto facendo qualcosa di sbagliato qui dal punto di vista dell'applicazione web?
È stato utile?

Soluzione

Quando eseguiamo la stessa funzione su un'app desktop (ad esempio un'app WPF), possiamo vedere una differenza di prestazioni tangibili. L'interfaccia utente non è bloccata, i dati vengono spinti sullo schermo immediatamente quando è disponibile.

Questo è un malinteso comune. Il metodo che hai pubblicato è leggermente Più lentamente rispetto al suo equivalente sincrono, ma in un'app dell'interfaccia utente è più reattivo, e quindi appare più performanto.

In uno scenario ASP.NET, la pagina viene resa solo quando tutte le richieste asincrone sono state completate; Ecco perché non vedi una differenza.

Ce la puoi fare in realtà Più performanti parallelizzando le tue richieste:

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;

Ciò migliorerà le prestazioni sia per Desktop che per ASP.NET.

Il tuo codice originale utilizza la composizione seriale (uno await Al tempo). In questo caso, il codice è in esecuzione in modo asincrono, ma la richiesta non è completata più velocemente. C'è ancora un vantaggio da utilizzare async/await In questo modo: la richiesta non lega un thread ASP.NET, quindi il tuo servizio può aumentare di più. Nel mondo dell'interfaccia utente, il thread dell'interfaccia utente non viene legato, quindi è più reattivo. Ma sia per asp.net che per l'interfaccia utente, il totale tempo di completare il GetMoviesM Il metodo non è ridotto rendendolo async.

Composizione parallela (usando Task.WhenAll o Task.WhenAny) consente GetMoviesM Nel suo insieme per funzionare più velocemente, poiché fa le richieste in parallelo. Inoltre, ottieni i benefici del thread sopra menzionati.

In questo caso, il numero di processori non ha importanza. Entrano in gioco solo quando fai elaborazione sul pool di thread (ad es. Task.Run), non quando si fa I/O. (Questa è una semplificazione, ma abbastanza vera).

Altri suggerimenti

Alcuni commenti.

In primo luogo, WebClient per impostazione predefinita apre solo 2 connessioni per server, per sessione. Questo ovviamente avrà un impatto sulla tua capacità di ridimensionamento, quindi potresti voler cambiarlo [vedi Come posso rimuovere a livello di programmazione il limite di connessione 2 in webclient ]

In secondo luogo, non sono sicuro che ci sia alcun vantaggio nell'uso di Async sia nel metodo del controller sia all'interno del metodo QueryMoviesasync.

In terzo luogo, WebClient implementa Idisposable, quindi dovresti usarlo con un'istruzione Using (..). Non farlo può anche influire sulla scalabilità.

Date tutte le modifiche di cui sopra e per rispondere alla domanda originale, sì, il codice dovrebbe ridimensionare su più processori/core in fase di esecuzione in quanto è il valore predefinito per ASP.NET/IIS

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top