Esporta (0) Stampa
Espandi tutto
Il presente articolo è stato tradotto manualmente. Passare il puntatore sulle frasi nell'articolo per visualizzare il testo originale.
Traduzione
Originale

Utilizzo di un controller asincrono in MVC ASP.NET

La classe AsyncController consente di scrivere metodi di azione asincroni. È possibile utilizzare metodi di azione asincroni per le richieste a esecuzione prolungata non associate alla CPU. Ciò impedisce al server Web di bloccare l'esecuzione del lavoro mentre viene elaborata la richiesta. Un utilizzo tipico della classe AsyncController è quello relativo alle chiamate ai servizi Web a esecuzione prolungata.

In questo argomento sono incluse le seguenti sezioni:

Come complemento a questo argomento è disponibile un progetto di Visual Studio con codice sorgente: Download (la pagina potrebbe essere in inglese).

Sul server Web, .NET Framework gestisce un pool di thread utilizzati per soddisfare le richieste ASP.NET. Quando arriva una richiesta, viene inviato un thread dal pool per elaborarla. Se la richiesta viene elaborata in modo sincrono, il thread che elabora la richiesta viene bloccato durante l'elaborazione e tale thread non può soddisfare un'altra richiesta.

Questo potrebbe non essere un problema, perché è possibile ingrandire il pool di thread in modo da includere numerosi thread bloccati. Tuttavia, il numero di thread nel pool di thread è limitato. Nelle applicazioni di grandi dimensioni che elaborano più richieste a esecuzione prolungata simultanee, tutti i thread disponibili potrebbero essere bloccati. Questa condizione è nota come mancanza di risorse dei thread. Quando viene raggiunta questa condizione, il server Web mette in coda le richieste. Se la coda di richieste si riempie, il server Web rifiuta le richieste con lo stato HTTP 503 (Server occupato).

Nelle applicazioni dove si potrebbe verificare una mancanza di risorse dei thread, è possibile configurare azioni da elaborare in modo asincrono. L'elaborazione di una richiesta asincrona ha la stessa durata di una richiesta sincrona. Ad esempio, se una richiesta effettua una chiamata di rete che dura due secondi, la richiesta impiegherà due secondi sia che venga eseguita in modo sincrono che in modo asincrono. Tuttavia, durante una chiamata asincrona, al server non viene impedito di rispondere ad altre richieste mentre la prima richiesta viene completata. Pertanto, le richieste asincrone impediscono l'accodamento quando molte di esse richiamano operazioni a esecuzione prolungata.

Quando viene richiamata un'azione asincrona, vengono effettuate le operazioni seguenti:

  1. Il server Web ottiene un thread dal pool di thread (thread di lavoro) e lo pianifica per gestire una richiesta in arrivo. Il thread di lavoro inizia un'operazione asincrona.

  2. Il thread di lavoro viene restituito al pool di thread per soddisfare un'altra richiesta Web.

  3. Quando l'operazione asincrona viene completata, viene inviata una notifica ad ASP.NET.

  4. Il server Web ottiene un thread di lavoro dal pool di thread (il thread potrebbe essere diverso da quello che ha avviato l'operazione asincrona) per elaborare il resto della richiesta, inclusa l'esecuzione del rendering della risposta.

L'illustrazione seguente mostra il modello asincrono.

Pipeline asincrona

In questa sezione vengono elencate le linee guida relative all'utilizzo dei metodi di azione sincroni o asincroni. Si tenga presente che si tratta di semplici linee guida. È necessario esaminare ogni applicazione singolarmente per determinare se i metodi di azione asincroni consentono di migliorare le prestazioni.

In generale, utilizzare pipeline sincrone quando le condizioni seguenti sono vere:

  • Le operazioni sono semplici o a esecuzione breve.

  • La semplicità è più importante dell'efficienza.

  • Le operazioni sono principalmente operazioni della CPU anziché operazioni che comportano un sovraccarico esteso del disco o della rete. L'utilizzo di metodi di azione asincroni per operazioni associate alla CPU non fornisce alcun vantaggio e comporta un maggiore sovraccarico.

In generale, utilizzare pipeline asincrone quando le condizioni seguenti sono vere:

  • Le operazioni sono associate alla rete o con vincoli di I/O anziché essere associate alla CPU.

  • I test mostrano che le operazioni di blocco sono un collo di bottiglia per le prestazioni del sito e che IIS può soddisfare più richieste tramite metodi di azione asincroni per queste chiamate di blocco.

  • Il parallelismo è più importante della semplicità del codice.

  • Si desidera fornire un meccanismo che consente agli utenti di annullare una richiesta a esecuzione prolungata.

L'esempio scaricabile mostra come utilizzare efficacemente metodi di azione asincroni. Il programma di esempio chiama il metodo Sleep per simulare un processo a esecuzione prolungata. Sono poche le applicazioni di produzione che possono mostrare vantaggi così ovvi derivanti dall'utilizzo dei metodi di azione asincroni.

È necessario testare le applicazioni per determinare se i metodi asincroni forniscono un vantaggio a livello di prestazioni. In alcuni casi potrebbe essere meglio aumentare il numero massimo di richieste simultanee IIS per CPU e il numero massimo di thread simultanei per CPU. Per ulteriori informazioni sulla configurazione dei thread di ASP.NET, vedere l'intervento ASP.NET Thread Usage on IIS 7.0 and 6.0 sul blog di Thomas Marquardt. Per ulteriori informazioni su quando effettuare chiamate di database asincrone, vedere l'intervento Should my database calls be Asynchronous? sul blog di Rick Anderson.

Poche applicazioni richiedono che tutti i metodi di azione siano asincroni. Spesso, la conversione di alcuni metodi di azione sincroni in metodi asincroni fornisce il migliore aumento dell'efficienza per la quantità di lavoro richiesta.

Nel codice di esempio seguente viene mostrato un metodo di azione sincrono utilizzato per visualizzare notizie da un controller di portale. La richiesta Portal/News?city=Seattle consente di visualizzare le notizie relative a Seattle.

public class PortalController: Controller {
    public ActionResult News(string city) {
        NewsService newsService = new NewsService();
        ViewStringModel headlines = newsService.GetHeadlines(city);
        return View(headlines);
    }
}

Nell'esempio seguente viene illustrato il metodo di azione News riscritto come metodo asincrono.

public class PortalController : AsyncController {
    public void NewsAsync(string city) {

        AsyncManager.OutstandingOperations.Increment();
        NewsService newsService = new NewsService();
        newsService.GetHeadlinesCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["headlines"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };
        newsService.GetHeadlinesAsync(city);
    }

    public ActionResult NewsCompleted(string[] headlines) {
        return View("News", new ViewStringModel
        {
            NewsHeadlines = headlines
        });
    }
}

Per convertire un metodo di azione sincrono in un metodo di azione asincrono, eseguire i passaggi seguenti:

  1. Anziché derivare il controller da Controller, derivarlo da AsyncController. I controller che derivano da AsyncController consentono ad ASP.NET di elaborare richieste asincrone e possono comunque soddisfare metodi di azione sincroni.

  2. Creare due metodi per l'azione. Il metodo che inizia il processo asincrono deve avere un nome costituito dall'azione e dal suffisso "Async". Il metodo richiamato al termine del processo asincrono (metodo di callback) deve avere un nome costituito dall'azione e dal suffisso "Completed". Nell'esempio precedente, il metodo News è stato trasformato in due metodi: NewsAsync e NewsCompleted.

    Il metodo NewsAsync restituisce void (nessun valore in Visual Basic). Il metodo NewsCompleted restituisce un'istanza ActionResult. Anche se l'azione è costituita da due metodi, l'accesso a essa viene eseguito utilizzando lo stesso URL di un metodo di azione sincrono, ad esempio, Portal/News?city=Seattle. Anche metodi quali RedirectToAction e RenderAction faranno riferimento al metodo di azione come News e non NewsAsync.

    I parametri passati a NewsAsync utilizzano i meccanismi di associazione di parametri normali. I parametri passati a NewsCompleted utilizzano il dizionario Parameters.

  3. Sostituire la chiamata sincrona nel metodo ActionResult originale con una chiamata asincrona nel metodo di azione asincrono. Nell'esempio precedente, la chiamata a newsService.GetHeadlines viene sostituita con una chiamata a newsService.GetHeadlinesAsync.

La classe NewsService utilizzata dal metodo NewsAsync è un esempio di servizio che espone metodi utilizzando un modello asincrono basato su eventi. Per ulteriori informazioni su questo modello, vedere Event-based Asynchronous Pattern Overview.

La proprietà OutstandingOperations notifica ad ASP.NET il numero di operazioni in sospeso. Ciò è necessario perché ASP.NET non è in grado di determinare quante operazioni sono state iniziate dal metodo di azione o quando tali operazioni vengono completate. Quando la proprietà OutstandingOperations è zero, ASP.NET completa l'operazione asincrona complessiva chiamando il metodo NewsCompleted.

Si noti quanto segue sui metodi di azione asincroni:

  • Se il nome dell'azione è Sample, il framework cercherà i metodi SampleAsync e SampleCompleted.

  • Le pagine di visualizzazione devono essere denominate Sample.aspx piuttosto che SampleAsync.aspx o SampleCompleted.aspx. Il nome dell'azione è Sample, non SampleAsync.

  • Un controller non può contenere un metodo asincrono denominato SampleAsync e un metodo sincrono denominato Sample. In caso contrario, viene generata un'eccezione AmbiguousMatchException perché il metodo di azione SampleAsync e il metodo di azione Sample dispongono della stessa firma della richiesta.

I metodi di azione asincroni sono utili quando un'azione deve eseguire diverse operazioni indipendenti. Ad esempio, un sito portale potrebbe mostrare non solo notizie, ma anche informazioni sullo sport, il tempo, i titoli azionari e di altro tipo.

Nell'esempio seguente viene illustrata una versione sincrona del metodo di azione Index del portale di notizie.

public ActionResult IndexSynchronous( string city ) {
    
    NewsService newsService = new NewsService();
    string[] headlines = newsService.GetHeadlines();

    SportsService sportsService = new SportsService();
    string[] scores = sportsService.GetScores();

    WeatherService weatherService = new WeatherService();
    string[] forecast = weatherService.GetForecast();

    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}

Le chiamate a ogni servizio vengono effettuate in sequenza. Pertanto, il tempo necessario per rispondere alla richiesta è la somma di ogni chiamata al servizio più una piccola quantità di sovraccarico. Ad esempio, se le chiamate impiegano 400, 500 e 600 millisecondi, il tempo di risposta totale sarà leggermente superiore a 1,5 secondi. Tuttavia, se le chiamate al servizio vengono effettuate in modo asincrono (in parallelo), il tempo di risposta totale sarà leggermente superiore a 600 millisecondi, corrispondente alla durata dell'attività più lunga.

Nell'esempio seguente viene illustrata una versione asincrona del metodo di azione Index del portale di notizie.

public void IndexAsync(string city) {
    AsyncManager.OutstandingOperations.Increment(3);

    NewsService newsService = new NewsService();
    newsService.GetHeadlinesCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["headlines"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    newsService.GetHeadlinesAsync();

    SportsService sportsService = new SportsService();
    sportsService.GetScoresCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["scores"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    sportsService.GetScoresAsync();

    WeatherService weatherService = new WeatherService();
    weatherService.GetForecastCompleted += (sender, e) =>
    {
        AsyncManager.Parameters["forecast"] = e.Value;
        AsyncManager.OutstandingOperations.Decrement();
    };
    weatherService.GetForecastAsync();
}

public ActionResult IndexCompleted(string[] headlines, string[] scores, string[] forecast) {
    return View("Common", new PortalViewModel  {
        NewsHeadlines = headlines,
        SportsScores = scores,
        Weather = forecast
    });
}          
}

Nell'esempio precedente, il metodo Increment viene chiamato con il parametro 3 perché vi sono tre operazioni asincrone.

Se si desidera applicare attributi a un metodo di azione asincrono, applicarli al metodo AzioneAsync anziché al metodo AzioneCompleted. Gli attributi del metodo AzioneCompleted vengono ignorati.

Sono stati aggiunti due nuovi attributi: AsyncTimeoutAttribute e NoAsyncTimeoutAttribute. Questi attributi consentono di controllare il periodo di timeout asincrono.

Se un metodo di azione asincrono chiama un servizio che espone metodi tramite il modello BeginMethod/EndMethod, il metodo di callback (ovvero, il metodo passato come parametro di callback asincrono al metodo Begin) potrebbe essere eseguito su un thread che non si trova sotto il controllo di ASP.NET. In tal caso, HttpContext.Current sarà null e nell'applicazione potrebbero verificarsi situazioni di race condition durante l'accesso a membri della classe AsyncManager quale Parameters. Per assicurarsi di disporre dell'accesso all'istanza HttpContext.Current ed evitare la race condition, è possibile ripristinare HttpContext.Current chiamando Sync() dal metodo di callback.

Se il callback viene completato in modo sincrono, verrà eseguito su un thread che si trova sotto il controllo di ASP.NET e le operazioni verranno serializzate in modo da evitare problemi di concorrenza. Una chiamata a Sync() da un thread che si trova già sotto il controllo di ASP.NET genera un comportamento indefinito.

Il metodo AzioneCompleted sarà sempre chiamato su un thread che si trova sotto il controllo di ASP.NET. Pertanto, non chiamare Sync() da tale metodo.

Il callback che si passa al metodo Begin può essere chiamato utilizzando un thread che si trova sotto il controllo di ASP.NET. Pertanto, è necessario verificare questa condizione prima di chiamare Sync(). Se l'operazione è stata completata in modo sincrono, ovvero, se CompletedSynchronously è true, il callback è in esecuzione sul thread originale e non è necessario chiamare Sync(). Se l'operazione è stata completata in modo asincrono, ovvero, se CompletedSynchronously è false, il callback è in esecuzione su un pool di thread o su un thread della porta di completamento di I/O ed è necessario chiamare Sync().

Per ulteriori informazioni sul modello BeginMethod/EndMethod, vedere Asynchronous Programming Overview e l'intervento Using the BeginMethod/EndMethod pattern with MVC sul blog di Rick Anderson.

Nella tabella riportata di seguito sono elencate le classi principali per i metodi di azione asincroni.

Classe

Descrizione

AsyncController

Fornisce la classe base per i controller asincroni.

AsyncManager

Fornisce le operazioni asincrone per la classe AsyncController.

Aggiunte alla community

AGGIUNGI
Microsoft sta conducendo un sondaggio in linea per comprendere l'opinione degli utenti in merito al sito Web di MSDN. Se si sceglie di partecipare, quando si lascia il sito Web di MSDN verrà visualizzato il sondaggio in linea.

Si desidera partecipare?
Mostra:
© 2015 Microsoft