Quando dovrei usare i controller asincroni in ASP.NET MVC?


216

Ho dei dubbi sull'utilizzo di azioni asincrone in ASP.NET MVC. Quando migliora le prestazioni delle mie app e quando no ?

  1. È utile utilizzare l'azione asincrona ovunque in ASP.NET MVC?
  2. Per quanto riguarda i metodi attendibili: devo usare parole chiave asincrone / wait quando voglio interrogare un database (tramite EF / NHibernate / altro ORM)?
  3. Quante volte posso usare le parole chiave wait per interrogare il database in modo asincrono in un unico metodo di azione?

Risposte:


109

I metodi di azione asincrona sono utili quando un'azione deve eseguire diverse operazioni indipendenti di lunga durata.

Un uso tipico per la classe AsyncController sono chiamate di servizi Web a esecuzione prolungata.

Le chiamate del mio database devono essere asincrone?

Il pool di thread IIS può spesso gestire molte più richieste di blocco simultanee rispetto a un server di database. Se il database è il collo di bottiglia, le chiamate asincrone non accelereranno la risposta del database. Senza un meccanismo di limitazione, inviare in modo efficiente più lavoro a un server di database sopraffatto utilizzando chiamate asincrone semplicemente sposta più del carico sul database. Se il tuo DB è il collo di bottiglia, le chiamate asincrone non saranno il proiettile magico.

Si dovrebbe dare un'occhiata a 1 e 2 referenze

Derivato dai commenti di @PanagiotisKanavos:

Inoltre, asincrono non significa parallelo. L'esecuzione asincrona libera un thread threadpool prezioso dal blocco per una risorsa esterna, senza costi di complessità o prestazioni. Ciò significa che la stessa macchina IIS può gestire più richieste simultanee, non che verrà eseguita più velocemente.

Dovresti anche considerare che il blocco delle chiamate inizia con uno spinwait ad alta intensità di CPU. Durante i periodi di stress, il blocco delle chiamate comporterà ritardi crescenti e il riciclo del pool di app. Le chiamate asincrone semplicemente evitano questo


27
Per quanto riguarda asincrono, IIS e il database, il post sul blog è estremamente vecchio e le conclusioni tratte qui sono errate. IIS deve caricare un carico molto maggiore rispetto al database, i thread di thread pool sono limitati e una lunga coda di richieste può portare a 500. Tutto ciò che libera un thread in attesa di IO è vantaggioso. Anche i collegamenti non riguardano ciò che l'OP ha chiesto. In effetti gli stessi autori hanno scritto che dovresti usare metodi DB asincroni ovunque tu sia
Panagiotis Kanavos

30
Inoltre, asincrono non significa parallelo. L'esecuzione asincrona libera un thread threadpool prezioso dal blocco per una risorsa esterna, senza costi di complessità o prestazioni. Ciò significa che la stessa macchina IIS può gestire più richieste simultanee, non che verrà eseguita più velocemente.
Panagiotis Kanavos,

5
Dovresti anche considerare che il blocco delle chiamate inizia con uno spinwait ad alta intensità di CPU. Durante i periodi di stress, il blocco delle chiamate comporterà ritardi crescenti e il riciclo del pool di app. Le chiamate asincrone semplicemente evitano questo
Panagiotis Kanavos,

1
Poiché questa è la risposta accettata e quindi al top per la maggior parte delle persone, potrebbe essere saggio rimuovere il secondo primo paragrafo relativo ai tempi di esecuzione e al funzionamento in parallelo, che non ha nulla a che fare con l'asincrono. Mi rendo conto che alla fine c'è una nota, ma è abbastanza fuorviante.
Rob

1
L'esecuzione asincrona libera un thread solo nei casi in cui è veramente asincrono fino alla fine dell'implementazione e utilizza un meccanismo di callback per segnalare la disponibilità e far girare un nuovo thread per cambiare il contesto ed elaborare il risultato. Quindi, non sono sicuro in quali casi questo cambio di contesto / la creazione di una nuova discussione è più efficiente della semplice attesa di risultati in una singola discussione? Le chiamate a servizi Web di lunga durata sono comunque una cattiva idea, preferisco scaricarle su un servizio in background affidabile e lasciare che il client controlli i risultati, che potrebbero arrivare in pochi minuti, ore o giorni.
JustAMartin,

229

Il mio articolo MSDN sull'argomento potrebbe essere utile ; Ho preso molto spazio in quell'articolo che descrive quando dovresti usare asyncsu ASP.NET, non solo su come usare asyncsu ASP.NET.

Ho dei dubbi sull'utilizzo di azioni asincrone in ASP.NET MVC. Quando migliora le prestazioni delle mie app e quando no.

Per prima cosa, capisci che async/ awaitriguarda la liberazione dei thread . Sulle applicazioni GUI, si tratta principalmente di liberare il thread della GUI in modo che l'esperienza dell'utente sia migliore. Sulle applicazioni server (incluso ASP.NET MVC), si tratta principalmente di liberare il thread di richiesta in modo che il server possa scalare.

In particolare, non:

  • Rendi più veloci le tue richieste individuali. In effetti, completeranno (solo un pochino) un po 'più lentamente.
  • Tornare al chiamante / browser quando si preme un await. await"cede" solo al pool di thread ASP.NET, non al browser.

La prima domanda è: è utile utilizzare l'azione asincrona ovunque in ASP.NET MVC?

Direi che è buono usarlo ovunque tu stia facendo I / O. Tuttavia, potrebbe non essere necessariamente utile (vedi sotto).

Tuttavia, è male usarlo per metodi associati alla CPU. A volte gli sviluppatori pensano di poter ottenere i vantaggi asyncsemplicemente chiamando i Task.Runloro controller, e questa è un'idea orribile. Perché quel codice finisce per liberare il thread di richiesta prendendo un altro thread, quindi non c'è alcun vantaggio (e in effetti, stanno prendendo la penalità di switch di thread aggiuntivi)!

Devo usare parole chiave asincrone / attendi quando voglio interrogare il database (tramite EF / NHibernate / altro ORM)?

È possibile utilizzare qualsiasi metodo attendibile disponibile. In questo momento la maggior parte dei principali giocatori supporta async, ma ce ne sono alcuni che non lo supportano . Se il tuo ORM non supporta async, quindi non provare ad avvolgerlo Task.Runo cose del genere (vedi sopra).

Si noti che ho detto "si può usare". Se stai parlando di ASP.NET MVC con un singolo back-end di database, allora (quasi sicuramente) non otterrai alcun vantaggio di scalabilità async. Questo perché IIS è in grado di gestire richieste più simultanee rispetto a una singola istanza di SQL Server (o altri RDBMS classici). Tuttavia, se il back-end è più moderno - un cluster di server SQL, SQL Azure, NoSQL, ecc - e il vostro backend possono scalare, e il collo di bottiglia è la scalabilità di IIS, quindi è possibile ottenere un beneficio scalabilità da async.

Terza domanda: quante volte posso usare parole chiave wait per interrogare il database in modo asincrono in UN metodo a singola azione?

Quanti ne vuoi. Tuttavia, si noti che molti ORM hanno una regola per operazione per connessione. In particolare, EF consente una sola operazione per DbContext; questo è vero se l'operazione è sincrona o asincrona.

Inoltre, tieni presente di nuovo la scalabilità del tuo backend. Se stai colpendo una singola istanza di SQL Server e IIS è già in grado di mantenere SQL Server a piena capacità, raddoppiare o triplicare la pressione su SQL Server non ti aiuterà affatto.


2
@seebiscuit: quando si esegue l'I / O (asincrono corretto), non vengono utilizzati thread. Ho un post sul blog che approfondisce i dettagli.
Stephen Cleary,

2
Quante richieste può gestire IIS e una singola istanza di SQL Server? Come posso trovare questi numeri?
MOD

1
@MOD: dipende dalla configurazione e dall'hardware. L'unico modo per trovare questi numeri è eseguire un test di carico sul proprio server.
Stephen Cleary,

1
@Flezcano: metodi che vengono eseguiti completamente sulla CPU. Vale a dire, non eseguono I / O o blocchi.
Stephen Cleary,

1
Mi dispiace signore, ma NON accetterò questa domanda di 2 righe che sto cercando di capire. Quando stavo cercando questa cosa come se avessi 1 endpoint API web che viene chiamato da 1000 utenti contemporaneamente, allora questo end point sarebbe in grado server di ciascuna di queste mille richieste o dovrei renderlo asincrono per gestire 1000 richieste?
Learning-Overthinker-Confused

21

è utile utilizzare l'azione asincrona ovunque in ASP.NET MVC?

Come al solito in programmazione, dipende . C'è sempre un compromesso quando si percorre un certo percorso.

async-awaitbrilla in luoghi in cui sai che riceverai richieste simultanee al tuo servizio e vuoi essere in grado di ridimensionare bene. In che modo async-awaitaiuta a ridimensionare? Nel fatto che quando si richiama una chiamata IO asincrona in modo sincrono , come una chiamata di rete o colpire il database, il thread corrente responsabile dell'esecuzione viene bloccato in attesa del completamento della richiesta. Quando si utilizza async-await, si abilita il framework per creare una macchina a stati per l'utente che si assicura che dopo che la chiamata IO sia stata completata, il metodo continui a essere eseguito da dove era stato interrotto.

Una cosa da notare è che questa macchina a stati ha un leggero sovraccarico. Rendere un metodo asincrono non lo fa eseguire più velocemente , e questo è un fattore importante da capire e un'idea sbagliata che molte persone hanno.

Un'altra cosa da prendere in considerazione durante l'utilizzo async-awaitè il fatto che è asincrono fino in fondo , il che significa che vedrai asincroni penetrare nell'intero stack di chiamate, dall'alto verso il basso. Ciò significa che se vuoi esporre API sincrone, ti troverai spesso a duplicare una certa quantità di codice, poiché asincronizzazione e sincronizzazione non si mescolano molto bene.

Devo usare parole chiave asincrone / attendi quando voglio interrogare il database (tramite EF / NHibernate / altro ORM)?

Se si sceglie di seguire il percorso dell'utilizzo delle chiamate IO asincrone, allora sì, async-awaitsarà una buona scelta, poiché sempre più fornitori di database moderni espongono il metodo asincrono che implementa il TAP (Task Asynchronous Pattern).

Quante volte posso usare le parole chiave wait per interrogare il database in modo asincrono in UN metodo a singola azione?

Quante ne vuoi, purché segua le regole stabilite dal tuo provider di database. Non vi è alcun limite alla quantità di chiamate asincrone che è possibile effettuare. Se hai delle domande indipendenti le une dalle altre e che possono essere fatte contemporaneamente, puoi girare una nuova attività per ognuna e await Task.WhenAllaspettare di completarle entrambe.


2
Le chiamate db asincrone non vengono eseguite più rapidamente, ma consentono alla stessa macchina IIS di soddisfare molte più richieste perché i thread del thread pool non vengono sprecati. Riduce anche i costi della CPU perché il blocco delle chiamate inizia effettivamente con uno spinwait ad alta intensità di CPU prima del blocco effettivo. In situazioni di carico elevato questa può essere la differenza tra la macchina in difficoltà o in avaria e il riciclaggio
Panagiotis Kanavos,

@PanagiotisKanavos Lo so, non era chiaro da quello che ho detto?
Yuval Itzchakov,

1
La tua risposta non menziona le specifiche di IIS: lo scenario di fusione / riciclo è uno dei motivi principali per utilizzare l'asincronizzazione. Qualcuno che non l'ha sperimentato probabilmente non capirà che scale out wellpotrebbe significare your server won't die under load. O potrebbe essere un caso dionce burned ...
Panagiotis Kanavos,

7

I miei 5 centesimi:

  1. Utilizzare async/awaitse e solo se si esegue un'operazione IO, come DB o un servizio Web di servizio esterno.
  2. Preferisci sempre le chiamate asincrone al DB.
  3. Ogni volta che si esegue una query sul DB.

PS Ci sono casi eccezionali per il punto 1, ma per questo è necessario avere una buona conoscenza degli interni asincroni.

Come ulteriore vantaggio, è possibile effettuare alcune chiamate IO in parallelo, se necessario:

Task task1 = FooAsync(); // launch it, but don't wait for result
Task task2 = BarAsync(); // launch bar; now both foo and bar are running
await Task.WhenAll(task1, task2); // this is better in regard to exception handling
// use task1.Result, task2.Result

6

asyncLe azioni aiutano meglio quando le azioni eseguono alcune operazioni I \ O sul DB o alcune chiamate associate alla rete in cui il thread che elabora la richiesta verrà bloccato prima che ottenga risposta dal DB o dalla chiamata associata alla rete appena invocata. È meglio che tu li attenda con loro e migliorerà davvero la reattività della tua applicazione (perché meno thread di input / output ASP verranno bloccati in attesa del DB o di qualsiasi altra operazione simile). In tutte le mie applicazioni ogni volta che sono necessarie molte chiamate a DB le ho sempre avvolte in un metodo attendibile e l'ho chiamato con awaitparola chiave.


5

Come sapete, MVC supporta controller asincroni e dovreste approfittarne. Nel caso in cui il controller esegua un'operazione prolungata (potrebbe essere un I / O basato su disco o una chiamata di rete a un altro servizio remoto), se la richiesta viene gestita in modo sincrono, il thread IIS è sempre impegnato. Di conseguenza, il thread sta solo aspettando il completamento dell'operazione lunga. Può essere meglio utilizzato servendo altre richieste mentre l'operazione richiesta all'inizio è in corso. Ciò contribuirà a soddisfare più richieste simultanee. Il tuo servizio web sarà altamente scalabile e non si imbatterà facilmente nel problema C10k . È una buona idea usare async / wait per le query db. e sì, puoi usarli quante volte ritieni opportuno.

Dai un'occhiata qui per un consiglio eccellente.


3

La mia esperienza è che oggi molti sviluppatori usano async/awaitcome controller i controller.

Il mio suggerimento sarebbe: usalo solo quando sai che ti aiuterà .

Il motivo è che, come già accennato Stephen Cleary e altri, può introdurre problemi di prestazioni, piuttosto che risolverli, e ti aiuterà solo in uno scenario specifico:

  • Controllori ad alto traffico
  • Backend scalabile

2

È utile utilizzare l'azione asincrona ovunque in ASP.NET MVC?

È utile farlo ovunque sia possibile utilizzare un metodo asincrono, in particolare quando si hanno problemi di prestazioni a livello di processo di lavoro che si verificano per operazioni di calcolo e dati di massa. Altrimenti, non è necessario perché i test dell'unità dovranno essere lanciati.

Per quanto riguarda i metodi attendibili: devo usare parole chiave asincrone / wait quando voglio interrogare un database (tramite EF / NHibernate / altro ORM)?

Sì, è meglio utilizzare l'asincronizzazione per qualsiasi operazione DB possibile per evitare problemi di prestazioni a livello dei processi di lavoro. Si noti che EF ha creato molte alternative asincrone per la maggior parte delle operazioni, come ad esempio:

.ToListAsync()
.FirstOrDefaultAsync()
.SaveChangesAsync()
.FindAsync()

Quante volte posso usare le parole chiave wait per interrogare il database in modo asincrono in un unico metodo di azione?

Il cielo è il limite


La logica aziendale per la maggior parte delle app Web di solito richiede operazioni altamente sequenziali, quindi nella maggior parte dei casi è possibile saltare tra processi paralleli solo al livello più alto della gestione delle richieste Web / dei processi di lavoro, che comunque non gestisce direttamente le richieste del database. Vorrei davvero vedere alcuni esempi reali di un'applicazione Web tipica in cui sarebbe in realtà una buona idea elaborare query DB in modo asincrono.
JustAMartin,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.