Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa esercitazione illustra le nozioni di base per la creazione di un'applicazione Web Form asincrona ASP.NET usando Visual Studio Express 2012 per Web, una versione gratuita di Microsoft Visual Studio. È anche possibile usare Visual Studio 2012. In questa esercitazione sono incluse le sezioni seguenti.
- Modalità di elaborazione delle richieste da parte del pool di thread
- Scelta di metodi sincroni o asincroni
- Applicazione di esempio
- Pagina sincrona Gizmos
- Creazione di una pagina Gizmos asincrona
- Esecuzione di più operazioni in parallelo
- Uso di un token di annullamento
- Configurazione del server per chiamate al servizio Web a concorrenza elevata/latenza elevata
Per questa esercitazione è disponibile un esempio completo all'indirizzo
https://github.com/RickAndMSFT/Async-ASP.NET/ nel sito GitHub .
ASP.NET 4.5 Pagine Web in combinazione con .NET 4.5 consente di registrare metodi asincroni che restituiscono un oggetto di tipo Task. .NET Framework 4 ha introdotto un concetto di programmazione asincrona denominato Task e ASP.NET 4.5 supporta Task. Le attività sono rappresentate dal tipo di attività e dai tipi correlati nello spazio dei nomi System.Threading.Tasks . .NET Framework 4.5 si basa su questo supporto asincrono con le parole chiave await e asincrone che rendono l'uso di oggetti Task molto meno complessi rispetto agli approcci asincroni precedenti. La parola chiave await è sintatticamente abbreviata per indicare che un frammento di codice deve attendere in modo asincrono su un altro frammento di codice. La parola chiave asincrona rappresenta un hint che è possibile usare per contrassegnare i metodi come metodi asincroni basati su attività. La combinazione di await, async e l'oggetto Task semplifica notevolmente la scrittura di codice asincrono in .NET 4.5. Il nuovo modello per i metodi asincroni è denominato Modello asincrono basato su attività (TAP). Questa esercitazione presuppone una certa familiarità con il programma asincrono usando parole chiave await e asincrone e lo spazio dei nomi Task .
Per ulteriori informazioni sull'uso delle parole chiave await e async e dello spazio dei nomi Task, vedere i riferimenti seguenti.
- White paper: Asynchrony in .NET
- Domande frequenti su Async/Await
- Programmazione asincrona di Visual Studio
Modalità di elaborazione delle richieste da parte del pool di thread
Nel server Web, .NET Framework gestisce un pool di thread usati per gestire le richieste di ASP.NET. Quando arriva una richiesta, viene inviato un thread dal pool per elaborare tale richiesta. Se la richiesta viene elaborata in modo sincrono, il thread che elabora la richiesta è occupato durante l'elaborazione della richiesta e tale thread non può eseguire un'altra richiesta.
Questo potrebbe non essere un problema, perché il pool di thread può essere reso sufficientemente grande per contenere molti thread occupati. Tuttavia, il numero di thread nel pool di thread è limitato (il valore massimo predefinito per .NET 4.5 è 5.000). In applicazioni di grandi dimensioni con concorrenza elevata di richieste a esecuzione prolungata, tutti i thread disponibili potrebbero essere occupati. Questa condizione è nota come fame di thread. Quando viene raggiunta questa condizione, il server Web accoda le richieste. Se la coda delle richieste diventa piena, il server Web rifiuta le richieste con stato HTTP 503 (Server Troppo occupato). Il pool di thread CLR presenta limitazioni sulle iniezioni di nuovi thread. Se la concorrenza è bursty (ovvero, il sito Web può ottenere improvvisamente un numero elevato di richieste) e tutti i thread di richiesta disponibili sono occupati a causa di chiamate al backend con alta latenza, il tasso di iniezione limitato dei thread può far sì che l'applicazione risponda molto male. Inoltre, ogni nuovo thread aggiunto al pool di thread ha un sovraccarico (ad esempio 1 MB di memoria dello stack). Un'applicazione web che utilizza metodi sincroni per gestire chiamate con elevata latenza, dove il pool di thread raggiunge il valore massimo predefinito di .NET 4.5 di 5,000 thread, consumerebbe circa 5 GB di memoria in più rispetto a un'applicazione in grado di gestire le stesse richieste usando metodi asincroni e solo 50 thread. Quando si esegue un lavoro asincrono, non si usa sempre un thread. Ad esempio, quando si effettua una richiesta di servizio Web asincrona, ASP.NET non usa thread tra la chiamata al metodo asincrono e l'await. L'uso del pool di thread per gestire le richieste con una latenza elevata può causare un footprint di memoria elevato e un utilizzo insufficiente dell'hardware del server.
Elaborazione di richieste asincrone
Nelle applicazioni Web che all'avvio gestiscono un numero elevato di richieste simultanee o presentano un carico improvviso (in cui la concorrenza aumenta improvvisamente), rendere asincrone le chiamate al servizio Web aumenterà la velocità di risposta dell'applicazione. Una richiesta asincrona richiede lo stesso tempo necessario per l'elaborazione di una richiesta sincrona. Ad esempio, se una richiesta effettua una chiamata al servizio Web che richiede due secondi per il completamento, la richiesta richiede due secondi se viene eseguita in modo sincrono o asincrono. Tuttavia, durante una chiamata asincrona, un thread non viene bloccato a rispondere ad altre richieste mentre attende il completamento della prima richiesta. Pertanto, le richieste asincrone impediscono l'accodamento delle richieste e la crescita del pool di thread quando sono presenti molte richieste simultanee che richiamano operazioni a esecuzione prolungata.
Scelta di metodi sincroni o asincroni
Questa sezione elenca le linee guida per quando usare metodi sincroni o asincroni. Queste sono solo linee guida; esaminare singolarmente ogni applicazione per determinare se i metodi asincroni contribuiscono alle prestazioni.
In generale, usare metodi sincroni per le condizioni seguenti:
- Le operazioni sono semplici o a breve esecuzione.
- La semplicità è più importante dell'efficienza.
- Le operazioni sono principalmente operazioni della CPU anziché operazioni che comportano un sovraccarico di rete o disco esteso. L'uso di metodi asincroni sulle operazioni associate alla CPU non offre alcun vantaggio e comporta un sovraccarico maggiore.
In generale, usare metodi asincroni per le condizioni seguenti:
Stai chiamando servizi che possono essere consumati attraverso metodi asincroni e stai usando .NET 4.5 o versione successiva.
Le operazioni sono associate alla rete o associate a I/O anziché associate alla CPU.
Il parallelismo è più importante della semplicità del codice.
Si vuole fornire un meccanismo che consente agli utenti di annullare una richiesta a esecuzione prolungata.
Quando il vantaggio del cambio di thread supera il costo del cambio di contesto. In generale, è consigliabile creare un metodo asincrono se il metodo sincrono blocca il thread di richiesta ASP.NET mentre non esegue alcuna operazione. Effettuando la chiamata in modalità asincrona, il thread di richiesta ASP.NET non viene bloccato e non rimane inattivo in attesa del completamento della richiesta al servizio Web, permettendo di utilizzare le risorse in modo più efficiente.
Il test mostra che le operazioni di blocco sono un collo di bottiglia nelle prestazioni del sito e che IIS può eseguire più richieste usando metodi asincroni per queste chiamate di blocco.
L'esempio scaricabile mostra come usare metodi asincroni in modo efficace. L'esempio fornito è stato progettato per fornire una semplice dimostrazione della programmazione asincrona in ASP.NET 4.5. L'esempio non deve essere un'architettura di riferimento per la programmazione asincrona in ASP.NET. Il programma di esempio chiama i metodi Web API di ASP.NET, i quali chiamano Task.Delay per simulare chiamate di servizio Web a esecuzione prolungata. La maggior parte delle applicazioni di produzione non mostrerà tali vantaggi evidenti all'uso di metodi asincroni.
Poche applicazioni richiedono che tutti i metodi siano asincroni. Spesso, la conversione di alcuni metodi sincroni in metodi asincroni fornisce l'aumento migliore dell'efficienza per la quantità di lavoro necessaria.
Applicazione di esempio
È possibile scaricare l'applicazione di esempio dal https://github.com/RickAndMSFT/Async-ASP.NET sito GitHub . Il repository è costituito da tre progetti:
- WebAppAsync: il progetto Web Form ASP.NET che utilizza il servizio WebAPIpwg dell'API Web. La maggior parte del codice per questa esercitazione proviene dal progetto.
-
WebAPIpgw: il progetto api Web MVC 4 ASP.NET che implementa i
Products, Gizmos and Widgetscontroller. Fornisce i dati per il progetto WebAppAsync e il progetto Mvc4Async . - Mvc4Async: il progetto ASP.NET MVC 4 che contiene il codice usato in un'altra esercitazione. Esegue chiamate API Web al servizio WebAPIpwg .
Pagina sincrona Gizmos
Il codice seguente illustra il Page_Load metodo sincrono usato per visualizzare un elenco di gizmos. Per questo articolo, un gizmo è un dispositivo meccanico fittizio.
public partial class Gizmos : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var gizmoService = new GizmoService();
GizmoGridView.DataSource = gizmoService.GetGizmos();
GizmoGridView.DataBind();
}
}
Il codice seguente illustra il GetGizmos metodo del servizio gizmo.
public class GizmoService
{
public async Task<List<Gizmo>> GetGizmosAsync(
// Implementation removed.
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
}
Il GizmoService GetGizmos metodo passa un URI a un servizio HTTP dell'API Web ASP.NET che restituisce un elenco di dati gizmos. Il progetto WebAPIpgw contiene l'implementazione dei controller gizmos, widget e dell'API Web product.
L'immagine seguente mostra la pagina gizmos del progetto di esempio.
Creazione di una pagina Gizmos asincrona
L'esempio usa le nuove parole chiave async e await (disponibili in .NET 4.5 e Visual Studio 2012) per consentire al compilatore di gestire le trasformazioni complesse necessarie per la programmazione asincrona. Il compilatore consente di scrivere codice usando i costrutti sincroni del flusso di controllo C#e il compilatore applica automaticamente le trasformazioni necessarie per usare i callback per evitare thread di blocco.
ASP.NET pagine asincrone devono includere la direttiva Page con l'attributo Async impostato su "true". Il codice seguente mostra la direttiva Page con l'attributo Async impostato su "true" per la pagina GizmosAsync.aspx .
<%@ Page Async="true" Language="C#" AutoEventWireup="true"
CodeBehind="GizmosAsync.aspx.cs" Inherits="WebAppAsync.GizmosAsync" %>
Il codice seguente illustra il Gizmos metodo sincrono Page_Load e la GizmosAsync pagina asincrona. Se il tuo browser supporta l'elemento
protected void Page_Load(object sender, EventArgs e)
{
var gizmoService = new GizmoService();
GizmoGridView.DataSource = gizmoService.GetGizmos();
GizmoGridView.DataBind();
}
Versione asincrona:
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(GetGizmosSvcAsync));
}
private async Task GetGizmosSvcAsync()
{
var gizmoService = new GizmoService();
GizmosGridView.DataSource = await gizmoService.GetGizmosAsync();
GizmosGridView.DataBind();
}
Sono state applicate le modifiche seguenti per consentire che la GizmosAsync pagina sia asincrona.
- La direttiva Page deve avere l'attributo
Asyncimpostato su "true". - Il
RegisterAsyncTaskmetodo viene usato per registrare un'attività asincrona contenente il codice che viene eseguito in modo asincrono. - Il nuovo
GetGizmosSvcAsyncmetodo è contrassegnato con la parola chiave async, che indica al compilatore di generare callback per parti del corpo del metodo e di creare automaticamente unTaskche viene restituito. - "Async" è stato aggiunto al nome del metodo asincrono. L'aggiunta di "Async" non è obbligatoria, ma è la convenzione durante la scrittura di metodi asincroni.
- Il tipo restituito del nuovo
GetGizmosSvcAsyncmetodo èTask. Il tipo di ritorno diTaskindica il lavoro in corso e fornisce ai chiamanti del metodo un handle per attendere il completamento di un'operazione asincrona. - La parola chiave await è stata applicata alla chiamata al servizio Web.
- L'API del servizio Web asincrona è stata chiamata (
GetGizmosAsync).
All'interno del corpo del GetGizmosSvcAsync metodo viene chiamato un altro metodo GetGizmosAsync asincrono.
GetGizmosAsync restituisce immediatamente un Task<List<Gizmo>> oggetto che si completerà quando i dati saranno disponibili. Poiché non si vuole eseguire altre operazioni finché non si hanno i dati gizmo, il codice attende l'attività (usando la parola chiave await ). È possibile usare la parola chiave await solo nei metodi annotati con la parola chiave async .
La parola chiave await non blocca il thread fino al completamento dell'attività. Registra il resto del metodo come callback per il compito e restituisce immediatamente. Al termine dell'attività attesa, richiamerà il callback e riprenderà quindi l'esecuzione del metodo esattamente dove era stata interrotta. Per altre informazioni sull'uso delle parole chiave await e async e dello spazio dei nomi Task , vedere i riferimenti asincroni.
Il codice seguente illustra i GetGizmos metodi e GetGizmosAsync .
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
public async Task<List<Gizmo>> GetGizmosAsync()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
await webClient.DownloadStringTaskAsync(uri)
);
}
}
Le modifiche asincrone sono simili a quelle apportate in precedenza a GizmosAsync .
- La firma del metodo è stata annotata con la parola chiave asincrona , il tipo restituito è stato modificato in
Task<List<Gizmo>>e Async è stato aggiunto al nome del metodo. - La classe HttpClient asincrona viene usata invece della classe WebClient sincrona.
- La parola chiave await è stata applicata al metodo asincrono HttpClientGetAsync .
La seguente immagine mostra la vista asincrona del gizmo.
La presentazione dei dati gizmos nei browser è identica alla visualizzazione creata dalla chiamata sincrona. L'unica differenza è che la versione asincrona può essere più efficiente con carichi pesanti.
Note su RegisterAsyncTask
I metodi associati a RegisterAsyncTask verranno eseguiti immediatamente dopo PreRender.
Se si usano direttamente eventi di pagina void asincroni, come illustrato nel codice seguente:
protected async void Page_Load(object sender, EventArgs e) {
await ...;
// do work
}
non si ha più il controllo completo su quando gli eventi sono eseguiti. Ad esempio, se sia un file .aspx che un file .Master definiscono eventi Page_Load e uno o entrambi sono asincroni, l'ordine di esecuzione non può essere garantito. Si applica lo stesso ordine indeterminato per i gestori eventi (ad esempio async void Button_Click ).
Esecuzione di più operazioni in parallelo
I metodi asincroni hanno un vantaggio significativo rispetto ai metodi sincroni quando un'azione deve eseguire diverse operazioni indipendenti. Nell'esempio fornito, la pagina sincrona PWG.aspx(per Prodotti, Widget e Gizmos) visualizza i risultati di tre chiamate al servizio Web per ottenere un elenco di prodotti, widget e gizmos. Il progetto API Web ASP.NET che fornisce questi servizi usa Task.Delay per simulare la latenza o chiamate di rete lente. Quando il ritardo è impostato su 500 millisecondi, la pagina di PWGasync.aspx asincrona richiede più di 500 millisecondi per il completamento mentre la versione sincrona PWG richiede più di 1.500 millisecondi. La pagina PWG.aspx sincrona è illustrata nel codice seguente.
protected void Page_Load(object sender, EventArgs e)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var pwgVM = new ProdGizWidgetVM(
widgetService.GetWidgets(),
prodService.GetProducts(),
gizmoService.GetGizmos()
);
WidgetGridView.DataSource = pwgVM.widgetList;
WidgetGridView.DataBind();
ProductGridView.DataSource = pwgVM.prodList;
ProductGridView.DataBind();
GizmoGridView.DataSource = pwgVM.gizmoList;
GizmoGridView.DataBind();
stopWatch.Stop();
ElapsedTimeLabel.Text = String.Format("Elapsed time: {0}",
stopWatch.Elapsed.Milliseconds / 1000.0);
}
Il codice sottostante asincrono PWGasync è illustrato di seguito.
protected void Page_Load(object sender, EventArgs e)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
RegisterAsyncTask(new PageAsyncTask(GetPWGsrvAsync));
stopWatch.Stop();
ElapsedTimeLabel.Text = String.Format("Elapsed time: {0}",
stopWatch.Elapsed.Milliseconds / 1000.0);
}
private async Task GetPWGsrvAsync()
{
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var widgetTask = widgetService.GetWidgetsAsync();
var prodTask = prodService.GetProductsAsync();
var gizmoTask = gizmoService.GetGizmosAsync();
await Task.WhenAll(widgetTask, prodTask, gizmoTask);
var pwgVM = new ProdGizWidgetVM(
widgetTask.Result,
prodTask.Result,
gizmoTask.Result
);
WidgetGridView.DataSource = pwgVM.widgetList;
WidgetGridView.DataBind();
ProductGridView.DataSource = pwgVM.prodList;
ProductGridView.DataBind();
GizmoGridView.DataSource = pwgVM.gizmoList;
GizmoGridView.DataBind();
}
L'immagine seguente mostra la visualizzazione restituita dalla pagina PWGasync.aspx asincrona.
Uso di un token di annullamento
I metodi asincroni che restituiscono Tasksono annullabili, ovvero accettano un parametro CancellationToken quando ne viene fornito uno con l'attributo AsyncTimeout della direttiva Page . Il codice seguente mostra la pagina GizmosCancelAsync.aspx con un timeout di un secondo.
<%@ Page Async="true" AsyncTimeout="1"
Language="C#" AutoEventWireup="true"
CodeBehind="GizmosCancelAsync.aspx.cs"
Inherits="WebAppAsync.GizmosCancelAsync" %>
Il codice seguente illustra il file GizmosCancelAsync.aspx.cs .
protected void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(GetGizmosSvcCancelAsync));
}
private async Task GetGizmosSvcCancelAsync(CancellationToken cancellationToken)
{
var gizmoService = new GizmoService();
var gizmoList = await gizmoService.GetGizmosAsync(cancellationToken);
GizmosGridView.DataSource = gizmoList;
GizmosGridView.DataBind();
}
private void Page_Error(object sender, EventArgs e)
{
Exception exc = Server.GetLastError();
if (exc is TimeoutException)
{
// Pass the error on to the Timeout Error page
Server.Transfer("TimeoutErrorPage.aspx", true);
}
}
Nell'applicazione di esempio fornita, selezionando il collegamento GizmosCancelAsync viene chiamata la pagina GizmosCancelAsync.aspx e viene illustrato l'annullamento (per timeout) della chiamata asincrona. Poiché il tempo di ritardo è compreso in un intervallo casuale, potrebbe essere necessario aggiornare la pagina un paio di volte per ottenere il messaggio di errore di timeout.
Configurazione del server per chiamate al servizio Web a concorrenza elevata/latenza elevata
Per sfruttare i vantaggi di un'applicazione Web asincrona, potrebbe essere necessario apportare alcune modifiche alla configurazione del server predefinita. Tenere presente quanto segue durante la configurazione e il test di stress dell'applicazione Web asincrona.
Windows 7, Windows Vista, Window 8 e tutti i sistemi operativi client Windows hanno un massimo di 10 richieste simultanee. È necessario un sistema operativo Windows Server per visualizzare i vantaggi dei metodi asincroni con carico elevato.
Registrare .NET 4.5 con IIS da un prompt dei comandi con privilegi elevati usando il comando seguente:
%windir%\Microsoft.NET\Framework64 \v4.0.30319\aspnet_regiis -i
Consultare Strumento di registrazione IIS di ASP.NET (Aspnet_regiis.exe)Potrebbe essere necessario aumentare il limite della coda di HTTP.sys dal valore predefinito da 1.000 a 5.000. Se l'impostazione è troppo bassa, potresti vedere HTTP.sys rifiutare le richieste con uno stato HTTP 503. Per modificare il limite di coda HTTP.sys:
- Aprire Gestione IIS e passare al riquadro Pool di applicazioni.
- Fare clic con il pulsante destro del mouse sul pool di applicazioni di destinazione e selezionare Impostazioni avanzate.
- Nella finestra di dialogo Impostazioni avanzate modificare Lunghezza coda da 1.000 a 5.000.
Nota nelle immagini precedenti, .NET Framework è elencato come v4.0, anche se il pool di applicazioni usa .NET 4.5. Per comprendere questa discrepanza, vedere quanto segue:
Controllo delle versioni .NET e multitargeting : .NET 4.5 è un aggiornamento sul posto a .NET 4.0
Come impostare un'applicazione IIS o un pool di app per usare ASP.NET 3.5 anziché 2.0
Se l'applicazione usa servizi Web o System.NET per comunicare con un back-end tramite HTTP, potrebbe essere necessario aumentare l'elemento connectionManagement/maxconnection . Per ASP.NET applicazioni, questa funzionalità è limitata dalla funzionalità autoConfig a 12 volte il numero di CPU. Ciò significa che in un quad-proc è possibile avere al massimo 12 * 4 = 48 connessioni simultanee a un endpoint IP. Poiché questo è associato a autoConfig, il modo più semplice per aumentare
maxconnectionin un'applicazione ASP.NET consiste nell'impostare System.Net.ServicePointManager.DefaultConnectionLimit a livello di codice nel metodo fromApplication_Startnel file global.asax . Per un esempio, vedere il file di esempio.In .NET 4.5 il valore predefinito di 5000 per MaxConcurrentRequestsPerCPU deve essere corretto.