Cómo y cuándo usar 'async' y 'await'
Según tengo entendido, una de las principales cosas que async
hacemosawait
es hacer que el código sea fácil de escribir y leer, pero ¿usarlos es equivalente a generar subprocesos en segundo plano para realizar una lógica de larga duración?
Actualmente estoy probando el ejemplo más básico. Agregué algunos comentarios en línea. ¿Puedes aclararmelo?
// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
Task<int> access = DoSomethingAsync();
// task independent stuff here
// this line is reached after the 5 seconds sleep from
// DoSomethingAsync() method. Shouldn't it be reached immediately?
int a = 1;
// from my understanding the waiting should be done here.
int x = await access;
}
async Task<int> DoSomethingAsync()
{
// is this executed on a background thread?
System.Threading.Thread.Sleep(5000);
return 1;
}
Cuando se usa async
y await
el compilador genera una máquina de estado en segundo plano.
Aquí hay un ejemplo en el que espero poder explicar algunos de los detalles de alto nivel que están sucediendo:
public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here
//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}
Bien, entonces, ¿qué pasa aquí?
Task<int> longRunningTask = LongRunningOperationAsync();
comienza a ejecutarseLongRunningOperation
Se realiza un trabajo independiente, supongamos que
await longRunningTask
se alcanza el subproceso principal (ID de subproceso = 1).Ahora, si
longRunningTask
no ha terminado y todavía se está ejecutando,MyMethodAsync()
volverá a su método de llamada, por lo que el hilo principal no se bloquea. CuandolongRunningTask
termine, un subproceso de ThreadPool (puede ser cualquier subproceso) volverá aMyMethodAsync()
su contexto anterior y continuará la ejecución (en este caso, imprimiendo el resultado en la consola).
Un segundo caso sería que el longRunningTask
ya haya finalizado su ejecución y el resultado esté disponible. Al llegar await longRunningTask
ya tenemos el resultado por lo que el código seguirá ejecutándose en el mismo hilo. (en este caso imprimiendo el resultado a la consola). Por supuesto, este no es el caso del ejemplo anterior, donde hay un Task.Delay(1000)
involucrado.
Según tengo entendido, una de las cosas principales que hacen async y await es hacer que el código sea fácil de escribir y leer.
Su objetivo es hacer que el código asincrónico sea fácil de escribir y leer, sí.
¿Es lo mismo que generar subprocesos en segundo plano para realizar una lógica de larga duración?
De nada.
// No entiendo por qué este método debe marcarse como 'async'.
La async
palabra clave habilita la await
palabra clave. Por lo tanto, cualquier método que se utilice await
debe estar marcado async
.
// Esta línea se alcanza después de 5 segundos de suspensión del método DoSomethingAsync(). ¿No debería alcanzarse de inmediato?
No, porque async
los métodos no se ejecutan en otro hilo de forma predeterminada.
// ¿Esto se ejecuta en un hilo en segundo plano?
No.
Puede que mi async
/ await
introducción te resulte útil. Los documentos oficiales de MSDN también son inusualmente buenos (particularmente la sección TAP ) y el async
equipo publicó excelentes preguntas frecuentes .