In ASP.NET Core è meglio usare un metodo sincrono o asincrono?
Tl;dr; usa sempre la versione asincrona.
Durante lo sviluppo di applicazioni web con ASP.NET Core, capita di imbattersi tra versioni sincrone e asincrone dello stesso metodo, così come anche nelle keyword await
e .Result
.
In questo breve articolo mostrerò l’impatto pratico che hanno l’una e l’altra e la motivazione che sta dietro alla mia scelta personale.
Sincrono vs Asincrono in ASP.NET Core: gestione dei thread
Quando un processo ASP.NET Core parte, a seconda delle risorse disponibili nel sistema, vengono avviati un numero variabile di thread per gestire le richieste HTTP. Normalmente il thread è dedicato a una singola chiamata e resta occupato fino a quando ha finito di gestirla.
Ci sono però alcune chiamate (tutte?) che richiedono di accedere a risorse esterne come database, filesystem, api ecc.
Una chiamata sincrona è quella in cui il thread che gestisce la richiesta rimane in attesa fino a quando l’operazione non è completata. Questo significa che ogni richiesta sincrona manterrà un thread occupato per tutto il tempo di esecuzione, quando, invece di aspettare la risorsa esterna, potrebbe gestire altre richieste nel frattempo.
Utilizzando invece la controparte asincrona, il thread è libero di fare altro e tornerà a gestire la richiesta quando le operazioni su risorse esterne sono terminate.
L’esperimento e i risultati ottenuti
Per verificare praticamente gli impatti di questo comportamento ho creato una applicazione di test configurata per avere 2 soli thread. L’applicazione espone due endpoint “lenti” che rispondono dopo aver aspettato 2 secondi, uno in modo sincrono e uno asincrono.
L’esperimento consiste nel chiamare l’endpoint lento cercando di saturare i thread disponibili e contemporaneamente chiamare un endpoint veloce misurandone le performance, confrontando poi le misurazioni nel caso sincrono e asincrono.
Di seguito i risultati di 30 secondi di esperimento.
Scenario sync | Scenario async | |
Numero di richieste in 30 secondi | 14 | 245748 |
Durata media singola richiesta veloce | 2144ms | 0.08ms |
95 perc durata richiesta veloce | 2505ms | 0.139ms |
Conclusioni
Come ci si poteva aspettare, la versione asincrona offre vantaggi concreti in termini di performance e scalabilità ed è quindi preferibile. Ora però sai anche il perché!
Per chiunque volesse approfondire, il mio progetto di test è disponibile nel GitHub di Intré.