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
Testare l'invio dei messaggi con Event Hubs Data Explorer
Cambiare la chiave di partizionamento di Azure Cosmos DB
Utilizzare gRPC su App Service di Azure
Creare una custom property in GitHub
Creare una libreria CSS universale: i bottoni
Introduzione ai web component HTML
Conoscere il rendering Server o WebAssembly a runtime in Blazor
Utilizzare il metodo Index di LINQ per scorrere una lista sapendo anche l'indice dell'elemento
Utilizzare un numero per gestire la concorrenza ottimistica con SQL Server ed Entity Framework
Creare una libreria CSS universale - Rotazione degli elementi
Gestire eccezioni nei plugin di Semantic Kernel in ASP.NET Core Web API
Aggiornare a .NET 9 su Azure App Service