Se non volevi usare async / await all'interno del tuo metodo, ma " decoralo " ancora per poter usare la parola chiave wait dall'esterno, TaskCompletionSource.cs :
public static Task<T> RunAsync<T>(Func<T> function)
{
if (function == null) throw new ArgumentNullException(“function”);
var tcs = new TaskCompletionSource<T>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
T result = function();
tcs.SetResult(result);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
Da qui e qui
Per supportare un tale paradigma con i Task, abbiamo bisogno di un modo per mantenere la facciata del Task e la capacità di fare riferimento a un'operazione asincrona arbitraria come Task, ma per controllare la durata di quel Task secondo le regole dell'infrastruttura sottostante che fornisce il asincrono, e farlo in un modo che non costa in modo significativo. Questo è lo scopo di TaskCompletionSource.
Ho visto che viene utilizzato anche nel sorgente .NET ad es. WebClient.cs :
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<string>(address);
// Setup the callback event handler
UploadStringCompletedEventHandler handler = null;
handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
this.UploadStringCompleted += handler;
// Start the async operation.
try { this.UploadStringAsync(address, method, data, tcs); }
catch
{
this.UploadStringCompleted -= handler;
throw;
}
// Return the task that represents the async operation
return tcs.Task;
}
Infine, ho trovato utile anche quanto segue:
Mi viene sempre fatta questa domanda. L'implicazione è che ci deve essere qualche thread da qualche parte che sta bloccando la chiamata I / O alla risorsa esterna. Quindi, il codice asincrono libera il thread della richiesta, ma solo a spese di un altro thread altrove nel sistema, giusto? No, per niente. Per capire perché le richieste asincrone vengono ridimensionate, traccerò un esempio (semplificato) di una chiamata I / O asincrona. Supponiamo che una richiesta debba scrivere in un file. Il thread della richiesta chiama il metodo di scrittura asincrono. WriteAsync è implementato dalla Base Class Library (BCL) e utilizza le porte di completamento per il suo I / O asincrono. Pertanto, la chiamata WriteAsync viene passata al sistema operativo come scrittura asincrona dei file. Il sistema operativo comunica quindi con lo stack del driver, passando i dati per scrivere in un pacchetto di richieste I / O (IRP). Qui è dove le cose si fanno interessanti: Se un driver di dispositivo non è in grado di gestire immediatamente un IRP, deve gestirlo in modo asincrono. Quindi, il driver dice al disco di iniziare a scrivere e restituisce una risposta "in sospeso" al sistema operativo. Il sistema operativo passa tale risposta "in sospeso" al BCL e il BCL restituisce un'attività incompleta al codice di gestione delle richieste. Il codice di gestione delle richieste attende l'attività, che restituisce un'attività incompleta da quel metodo e così via. Infine, il codice di gestione delle richieste finisce per restituire un'attività incompleta ad ASP.NET e il thread della richiesta viene liberato per tornare al pool di thread. Il codice di gestione delle richieste attende l'attività, che restituisce un'attività incompleta da quel metodo e così via. Infine, il codice di gestione delle richieste finisce per restituire un'attività incompleta ad ASP.NET e il thread della richiesta viene liberato per tornare al pool di thread. Il codice di gestione delle richieste attende l'attività, che restituisce un'attività incompleta da quel metodo e così via. Infine, il codice di gestione delle richieste finisce per restituire un'attività incompleta ad ASP.NET e il thread della richiesta viene liberato per tornare al pool di thread.
Introduzione ad Async / Await su ASP.NET
Se l'obiettivo è migliorare la scalabilità (piuttosto che la reattività), tutto si basa sull'esistenza di un I / O esterno che offre l'opportunità di farlo.