Cada vez que necesite realizar una acción en un servidor remoto, su programa genera la solicitud, la envía y luego espera una respuesta. Usaré SaveChanges()
y SaveChangesAsync()
como ejemplo, pero lo mismo se aplica a Find()
yFindAsync()
.
Supongamos que tiene una lista myList
de más de 100 elementos que necesita agregar a su base de datos. Para insertar eso, su función se vería así:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
Primero crea una instancia de MyEDM
, agrega la lista myList
a la tabla MyTable
, luego llamaSaveChanges()
para persistir los cambios en la base de datos. Funciona como lo desea, los registros se confirman, pero su programa no puede hacer nada más hasta que finalice la confirmación. Esto puede llevar mucho tiempo dependiendo de lo que esté cometiendo. Si está realizando cambios en los registros, la entidad tiene que confirmarlos uno a la vez (¡una vez tuve que guardar 2 minutos para las actualizaciones)!
Para resolver este problema, puede hacer una de dos cosas. La primera es que puede iniciar un nuevo hilo para manejar el inserto. Si bien esto liberará el hilo de llamada para continuar ejecutándose, creó un nuevo hilo que simplemente se quedará allí y esperará. No hay necesidad de esa sobrecarga, y esto es lo async await
que resuelve el patrón.
Para operaciones de E / S, await
se convierte rápidamente en su mejor amigo. Tomando la sección de código de arriba, podemos modificarla para que sea:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
Es un cambio muy pequeño, pero tiene efectos profundos en la eficiencia y el rendimiento de su código. ¿Así que lo que pasa? El comienzo del código es el mismo, crea una instancia de MyEDM
y agrega sumyList
a MyTable
. ¡Pero cuando llamas await context.SaveChangesAsync()
, la ejecución del código vuelve a la función que llama! Entonces, mientras espera que todos esos registros se confirmen, su código puede continuar ejecutándose. Digamos que la función que contenía el código anterior tenía la firma de public async Task SaveRecords(List<MyTable> saveList)
, la función de llamada podría verse así:
public async Task MyCallingFunction()
{
Console.WriteLine("Function Starting");
Task saveTask = SaveRecords(GenerateNewRecords());
for(int i = 0; i < 1000; i++){
Console.WriteLine("Continuing to execute!");
}
await saveTask;
Console.Log("Function Complete");
}
Por qué tendrías una función como esta, no lo sé, pero lo que genera muestra cómo async await
funciona. Primero repasemos lo que sucede.
La ejecución ingresa MyCallingFunction
, Function Starting
luego Save Starting
se escribe en la consola y luego SaveChangesAsync()
se llama a la función . En este punto, la ejecución regresa MyCallingFunction
e ingresa al bucle for escribiendo 'Continuar con la ejecución' hasta 1000 veces. CuandoSaveChangesAsync()
finaliza, la ejecución vuelve a la SaveRecords
función, escribiendo Save Complete
en la consola. Una vez que todo esté SaveRecords
completo, la ejecución continuará en el lugar MyCallingFunction
correcto donde estaba cuando SaveChangesAsync()
terminó. ¿Confuso? Aquí hay una salida de ejemplo:
Función de inicio
Guardar comenzando
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Continuando ejecutando!
....
¡Continuando ejecutando!
¡Guardar completo!
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Continuando ejecutando!
....
¡Continuando ejecutando!
¡Función completada!
O tal vez:
Función de inicio
Guardar comenzando
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Guardar completo!
¡Continuando ejecutando!
¡Continuando ejecutando!
¡Continuando ejecutando!
....
¡Continuando ejecutando!
¡Función completada!
Esa es la belleza de async await
su código puede continuar ejecutándose mientras espera que algo termine. En realidad, tendrías una función más como esta como tu función de llamada:
public async Task MyCallingFunction()
{
List<Task> myTasks = new List<Task>();
myTasks.Add(SaveRecords(GenerateNewRecords()));
myTasks.Add(SaveRecords2(GenerateNewRecords2()));
myTasks.Add(SaveRecords3(GenerateNewRecords3()));
myTasks.Add(SaveRecords4(GenerateNewRecords4()));
await Task.WhenAll(myTasks.ToArray());
}
Aquí, tiene cuatro funciones diferentes de guardar registros al mismo tiempo . MyCallingFunction
se completará mucho más rápido usando async await
que si las SaveRecords
funciones individuales fueran llamadas en serie.
Lo único que aún no he mencionado es la await
palabra clave. Lo que hace esto es detener la ejecución de la función actual hasta Task
que se complete lo que está esperando. Entonces, en el caso del original MyCallingFunction
, la línea Function Complete
no se escribirá en la consola hasta SaveRecords
que finalice la función.
En pocas palabras, si tiene una opción para usar async await
, debería hacerlo, ya que aumentará en gran medida el rendimiento de su aplicación.