In ASP.NET 4.5 possiamo eseguire operazioni asincrone sfruttando le keyword async e await di C# 5. Per esempio, in un controller ASP.NET MVC è sufficiente il codice in basso per poter eseguire la chiamata a SomeMethodAsync su un altro thread, liberando il thread di IIS:
public async Task<ActionResult> SomeAction() { await SomeMethodAsync(); return this.View(); } private async Task SomeMethodAsync() { await FirstOperationAsync(); await SecondOperationAsync(); }
Nell'esempio in alto, è il framework stesso a predisporre un SynchronizationContext che si occupa di effettuare il marshalling del codice nel contesto di richiesta originale, una volta che l'operazione asincrona è terminata. Questa funzionalità è sicuramente molto comoda, perchè ci permette di trascurare i dettagli su cosa sta accadendo dietro le quinte, rendendo il codice molto simile a quello che scriveremmo nel caso fosse sincrono.
Non bisogna mai dimenticare, però, che in realtà ciò che effettivamente accade è più complesso e può avere delle implicazioni non immediatamente prevedibili. Un esempio è il caso di un'operazione di tipo fire and forget, di cui cioé non attendiamo il risultato prima del completamento, come nell'esempio in basso:
public ActionResult SomeAction() { // non usiamo await, avviamo l'operazione senza attendere il completamento SomeMethodAsync(); // ritorniamo il risultato prima che l'invocazione // a SomeMethodAsync sia terminata return this.View(); } private async Task SomeMethodAsync() { // queste operazioni potrebbero generare un deadlock await FirstOperationAsync(); await SecondOperationAsync(); }
Quando l'esecuzione di FirstOperationAsync termina, il contesto di richiesta originale potrebbe non essere più disponibile, perché la view è stata già ritornata al browser e la richiesta si è effettivamente chiusa; ciò impedisce al SynchronizationContext di effettuare il marshalling, di fatto bloccando l'esecuzione di SomeMethodAsync.
Il modo corretto per ovviare a questo problema è quello di utilizzare l'opzione ConfigureAwait(false):
private async Task SomeMethodAsync() { await FirstOperationAsync().ConfigureAwait(false); await SecondOperationAsync().ConfigureAwait(false); }
In questo modo, il framework non tenterà di ripristinare il contesto di richiesta originale, consentendo quindi al task asincrono di terminare correttamente anche se questo non è più disponibile.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Evitare il flickering dei componenti nel prerender di Blazor 8
Gestione dell'annidamento delle regole dei layer in CSS
Creare una libreria CSS universale: Clip-path
Generare velocemente pagine CRUD in Blazor con QuickGrid
Aprire una finestra di dialogo per selezionare una directory in WPF e .NET 8
Sfruttare i KeyedService in un'applicazione Blazor in .NET 8
Paginare i risultati con QuickGrid in Blazor
Sfruttare MQTT in cloud e in edge con Azure Event Grid
Recuperare l'ultima versione di una release di GitHub
Eseguire script pre e post esecuzione di un workflow di GitHub
Usare una container image come runner di GitHub Actions
Gestione dei nomi con le regole @layer in CSS