Innanzitutto, chiariamo alcuni termini: "asincrono" ( async
) significa che potrebbe restituire il controllo al thread chiamante prima che inizi. In un async
metodo, quei punti di "rendimento" sonoawait
espressioni.
Questo è molto diverso dal termine "asincrono", poiché (mis) usato da anni dalla documentazione MSDN per significare "esegue su un thread in background".
Per confondere ulteriormente il problema, async
è molto diverso da "aspettabile"; ci sono alcuni async
metodi i cui tipi di restituzione non sono attendibili e molti metodi che restituiscono tipi attendibili che non lo sono async
.
Basta con quello che non sono ; ecco cosa sono :
- La
async
parola chiave consente un metodo asincrono (ovvero consente await
espressioni). async
i metodi possono restituire Task
, Task<T>
oppure (se necessario) void
.
- Qualsiasi tipo che segue un certo modello può essere attendibile. I tipi più comuni attesi sono
Task
e Task<T>
.
Quindi, se riformuliamo la tua domanda su "come posso eseguire un'operazione su un thread in background in modo che sia attendibile", la risposta è usare Task.Run
:
private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}
(Ma questo modello è un approccio scadente; vedi sotto).
Ma se la tua domanda è "come posso creare un async
metodo che può restituire al suo chiamante invece di bloccare", la risposta è dichiarare il metodo async
e usare await
per i suoi punti "cedenti":
private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}
Quindi, lo schema di base delle cose è che il async
codice dipenda da "attendibili" nelle sue await
espressioni. Questi "attendibili" possono essere altri async
metodi o solo metodi regolari che restituiscono gli aspetti attendibili. Metodi regolari ritorno Task
/ Task<T>
possono utilizzare Task.Run
per eseguire codice su un thread in background, o (più comunemente) che possono utilizzare TaskCompletionSource<T>
o uno dei suoi tasti di scelta rapida ( TaskFactory.FromAsync
, Task.FromResult
, ecc). Io non consiglio avvolgendo un intero metodo Task.Run
; i metodi sincroni dovrebbero avere firme sincrone e dovrebbe essere lasciato al consumatore se debba essere racchiuso in un Task.Run
:
private int DoWork()
{
return 1 + 2;
}
private void MoreSynchronousProcessing()
{
// Execute it directly (synchronously), since we are also a synchronous method.
var result = DoWork();
...
}
private async Task DoVariousThingsFromTheUIThreadAsync()
{
// I have a bunch of async work to do, and I am executed on the UI thread.
var result = await Task.Run(() => DoWork());
...
}
Ho async
/ await
intro sul mio blog; alla fine ci sono alcune buone risorse di follow-up. Anche i documenti MSDN per async
sono insolitamente buoni.