Primero, aclaremos algo de terminología: "asíncrono" ( async
) significa que puede ceder el control al hilo de llamada antes de que comience. En un async
método, esos puntos de "rendimiento" son await
expresiones.
Esto es muy diferente al término "asíncrono", ya que (mal) usado por la documentación de MSDN durante años significa "se ejecuta en un hilo de fondo".
Para confundir aún más el tema, async
es muy diferente de "espera"; Hay algunos async
métodos cuyos tipos de retorno no son esperados, y muchos métodos que devuelven tipos que no lo son async
.
Suficiente sobre lo que no son ; esto es lo que son :
- La
async
palabra clave permite un método asincrónico (es decir, permite await
expresiones). async
los métodos pueden regresar Task
, Task<T>
o (si debe hacerlo) void
.
- Cualquier tipo que siga un cierto patrón puede estar a la espera. Los tipos de espera más comunes son
Task
y Task<T>
.
Entonces, si reformulamos su pregunta a "¿cómo puedo ejecutar una operación en un subproceso en segundo plano de una manera que sea esperable", la respuesta es usar Task.Run
:
private Task<int> DoWorkAsync() // No async because the method does not need await
{
return Task.Run(() =>
{
return 1 + 2;
});
}
(Pero este patrón es un enfoque pobre; ver más abajo).
Pero si su pregunta es "¿cómo creo un async
método que pueda ceder el paso a su interlocutor en lugar de bloquearlo", la respuesta es declarar el método async
y usarlo await
para sus puntos de "ceder":
private async Task<int> GetWebPageHtmlSizeAsync()
{
var client = new HttpClient();
var html = await client.GetAsync("http://www.example.com/");
return html.Length;
}
Por lo tanto, el patrón básico de las cosas es que el async
código dependa de "esperables" en sus await
expresiones. Estos "esperables" pueden ser otros async
métodos o simplemente métodos regulares que devuelven objetos esperables. Métodos habituales que regresan Task
/ Task<T>
pueden utilizar Task.Run
para ejecutar código en un subproceso en segundo plano, o (más comúnmente) que pueden utilizar TaskCompletionSource<T>
o uno de sus accesos directos ( TaskFactory.FromAsync
, Task.FromResult
, etc.). Yo no recomiendo el embalaje de un método completo en Task.Run
; los métodos síncronos deben tener firmas síncronas, y debe dejarse en manos del consumidor si debe estar envuelto en 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());
...
}
Tengo una async
/ await
introducción en mi blog; al final hay algunos buenos recursos de seguimiento. Los documentos de MSDN para también async
son inusualmente buenos.