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
Cambiare la chiave di partizionamento di Azure Cosmos DB
Utilizzare Model as a Service su Microsoft Azure
Supportare lo HierarchyID di Sql Server in Entity Framework 8
Utilizzare EF.Constant per evitare la parametrizzazione di query SQL
Aprire una finestra di dialogo per selezionare una directory in WPF e .NET 8
Utilizzare QuickGrid di Blazor con Entity Framework
Gestione degli stili CSS con le regole @layer
Utilizzare il nuovo modello GPT-4o con Azure OpenAI
Creare una libreria CSS universale: Clip-path
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Utilizzare una qualunque lista per i parametri di tipo params in C#
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API