Oharra
Baimena behar duzu orria atzitzeko. Direktorioetan saioa has dezakezu edo haiek alda ditzakezu.
Baimena behar duzu orria atzitzeko. Direktorioak alda ditzakezu.
El modelo de programación asincrónica de tareas (TAP) proporciona una capa de abstracción sobre la codificación asincrónica típica. En este modelo, escribirá código como una secuencia de instrucciones, igual que de costumbre. La diferencia es que puede leer el código basado en tareas a medida que el compilador procesa cada instrucción y antes de empezar a procesar la siguiente instrucción. Para lograr este modelo, el compilador realiza muchas transformaciones para completar cada tarea. Algunas instrucciones pueden iniciar el trabajo y devolver un Task objeto que representa el trabajo en curso y el compilador debe resolver estas transformaciones. El objetivo de la programación asincrónica de tareas es habilitar el código que lee como una secuencia de instrucciones, pero se ejecuta en un orden más complicado. La ejecución se basa en la asignación de recursos externos y cuando se completan las tareas.
El modelo de programación asincrónica de tareas es análogo a cómo las personas proporcionan instrucciones para los procesos que incluyen tareas asincrónicas. En este artículo se usa un ejemplo con instrucciones para preparar el desayuno para mostrar cómo las async
palabras clave y await
facilitan la razón del código que incluye una serie de instrucciones asincrónicas. Las instrucciones para hacer un desayuno se pueden proporcionar como una lista:
- Verte una taza de café.
- Calentar una sartén y luego freír dos huevos.
- Freí tres rebanadas de tocino.
- Tosta dos rebanadas de pan.
- Unta mantequilla y mermelada en la tostada.
- Vierta un vaso de jugo de naranja.
Si tiene experiencia con la cocina, es posible que complete estas instrucciones de forma asincrónica. Empiezas a calentar la sartén para huevos y luego empiezas a freír el tocino. Pones el pan en la tostadora y luego empiezas a cocinar los huevos. En cada paso del proceso, inicia una tarea y, a continuación, realiza la transición a otras tareas que están listas para su atención.
El desayuno de cocina es un buen ejemplo de trabajo asincrónico que no es paralelo. Una persona (o hilo) puede manejar todas las tareas. Una persona puede hacer el desayuno de forma asincrónica iniciando la siguiente tarea antes de que se complete la tarea anterior. Cada tarea de cocción progresa independientemente de si alguien está viendo activamente el proceso. En cuanto empieces a calentar la sartén para los huevos, puedes comenzar a freír el tocino. Después de que el bacon comience a cocinar, puede poner el pan en la tostadora.
Para un algoritmo paralelo, necesitas varias personas que cocinen (o varios hilos). Una persona cocina los huevos, otra fríe el tocino, etc. Cada persona se centra en su tarea específica. Cada persona que está cocinando (o cada subproceso) se bloquea sincrónicamente esperando a que se complete la tarea actual: Bacon listo para darle la vuelta, pan listo para saltar en la tostadora, etc.
Considere la misma lista de instrucciones sincrónicas escritas como instrucciones de código de C#:
using System;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = FryEggs(2);
Console.WriteLine("eggs are ready");
Bacon bacon = FryBacon(3);
Console.WriteLine("bacon is ready");
Toast toast = ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");
private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");
private static Toast ToastBread(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
Task.Delay(3000).Wait();
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
private static Bacon FryBacon(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
Task.Delay(3000).Wait();
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
Task.Delay(3000).Wait();
Console.WriteLine("Put bacon on plate");
return new Bacon();
}
private static Egg FryEggs(int howMany)
{
Console.WriteLine("Warming the egg pan...");
Task.Delay(3000).Wait();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
Task.Delay(3000).Wait();
Console.WriteLine("Put eggs on plate");
return new Egg();
}
private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
}
Si interpreta estas instrucciones como haría un ordenador, el desayuno tarda unos 30 minutos en prepararse. La duración es la suma de los tiempos de tarea individuales. La computadora bloquea cada instrucción hasta que el trabajo se ha completado por completo, y luego continúa con la siguiente instrucción. Este enfoque puede tardar mucho tiempo. En el ejemplo de desayuno, el método del ordenador crea un desayuno no satisfactorio. Las tareas posteriores de la lista sincrónica, como tostar el pan, no se inician hasta que se completen las tareas anteriores. Algunos alimentos se frían antes de que el desayuno esté listo para servir.
Si desea que el equipo ejecute instrucciones de forma asincrónica, debe escribir código asincrónico. Al escribir programas cliente, quiere que la interfaz de usuario responda a la entrada del usuario. La aplicación no debe inmovilizar toda la interacción al descargar datos de la web. Al escribir programas de servidor, no desea bloquear los subprocesos que podrían estar atendiendo otras solicitudes. El uso de código sincrónico cuando existen alternativas asincrónicas daña la capacidad de escalar horizontalmente de forma menos costosa. Paga por los subprocesos que están bloqueados.
Las aplicaciones modernas correctas requieren código asincrónico. Sin soporte del lenguaje, la escritura de código asincrónico requiere callbacks, eventos de finalización u otros medios que pueden oscurecer la intención original del código. La ventaja del código sincrónico es la acción paso a paso que facilita el examen y la comprensión. Los modelos asincrónicos tradicionales le obligan a centrarse en la naturaleza asincrónica del código, no en las acciones fundamentales del código.
No bloquee, espere en su lugar
El código anterior resalta una práctica de programación desafortunada: Escritura de código sincrónico para realizar operaciones asincrónicas. El código impide que el subproceso actual realice cualquier otro trabajo. El código no interrumpe el subproceso mientras hay tareas en ejecución. El resultado de este modelo es similar a mirar en la tostadora después de poner el pan. Omite las interrupciones y no inicia otras tareas hasta que aparezca el pan. No sacas la mantequilla y la mermelada de la nevera. Es posible que te pierdas ver un fuego que empieza en la estufa. Quieres tostar el pan y manejar otras tareas al mismo tiempo. Lo mismo sucede con tu código.
Puede empezar actualizando el código para que el subproceso no bloquee mientras se ejecutan las tareas. La palabra clave await
proporciona una manera de no bloqueo para iniciar una tarea y luego continuar la ejecución cuando se completa la tarea. Una versión asincrónica simple del código de desayuno tiene el siguiente aspecto:
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Egg eggs = await FryEggsAsync(2);
Console.WriteLine("eggs are ready");
Bacon bacon = await FryBaconAsync(3);
Console.WriteLine("bacon is ready");
Toast toast = await ToastBreadAsync(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
El código actualiza los cuerpos de método originales de FryEggs
, FryBacon
y ToastBread
para devolver Task<Egg>
objetos , Task<Bacon>
y Task<Toast>
, respectivamente. Los nombres de método actualizados incluyen el sufijo "Async": FryEggsAsync
, FryBaconAsync
y ToastBreadAsync
. El Main
método devuelve el Task
objeto, aunque no tiene una return
expresión, que es por diseño. Para obtener más información, consulte Evaluación de una función asincrónica que devuelve void.
Nota:
El código actualizado aún no aprovecha las características clave de la programación asincrónica, lo que puede dar lugar a tiempos de finalización más cortos. El código procesa las tareas aproximadamente la misma cantidad de tiempo que la versión sincrónica inicial. Para ver las implementaciones completas del método, consulte la versión final del código más adelante en este artículo.
Vamos a aplicar el ejemplo de desayuno al código actualizado. El subproceso no se bloquea mientras los huevos o el tocino se están cocinando, pero el código tampoco inicia otras tareas hasta que se haya completado el trabajo actual. Todavía pones el pan en la tostadora y mira a la tostadora hasta que el pan aparezca, pero ahora puedes responder a interrupciones. En un restaurante donde se realizan varios pedidos, el cocinero puede comenzar un nuevo pedido mientras que otro ya está cocinando.
En el código actualizado, el subproceso que trabaja en la tarea del desayuno no se bloquea mientras espera a que se termine cualquier tarea iniciada que esté sin finalizar. Para algunas aplicaciones, este cambio es todo lo que necesita. Puede permitir que la aplicación admita la interacción del usuario mientras se descargan datos desde la web. En otros escenarios, es posible que desee iniciar otras tareas mientras espera a que se complete la tarea anterior.
Iniciar tareas simultáneamente
Para la mayoría de las operaciones, desea iniciar varias tareas independientes inmediatamente. A medida que completes cada tarea, inicias otros trabajos que estén listos para empezar. Al aplicar esta metodología al ejemplo de desayuno, puede preparar el desayuno más rápidamente. También tiene todo listo cerca de la misma hora, por lo que podrá disfrutar de un desayuno caliente.
La System.Threading.Tasks.Task clase y los tipos relacionados son clases que puede usar para aplicar este estilo de razonamiento a las tareas que están en curso. Este enfoque le permite escribir código que se parezca más a la forma en que se crea el desayuno en la vida real. Empiezas a cocinar los huevos, tocino y tostadas al mismo tiempo. A medida que cada alimento requiere acción, diriges tu atención a esa tarea, realizas la acción y, a continuación, esperas a que haya algo más que requiera tu atención.
En tu código, inicias una tarea y retienes el objeto Task que representa el trabajo. Use el await
método de la tarea para retrasar la acción en el trabajo hasta que el resultado esté listo.
Aplique estos cambios al código de desayuno. El primer paso es almacenar las tareas de las operaciones cuando se inician, en lugar de usar la await
expresión :
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Task<Bacon> baconTask = FryBaconAsync(3);
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Console.WriteLine("Breakfast is ready!");
Estas revisiones no ayudan a preparar el desayuno más rápido. La await
expresión se aplica a todas las tareas tan pronto como se inician. El siguiente paso es mover las await
expresiones del bacon y los huevos al final del método, antes de servir el desayuno:
Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");
Task<Egg> eggsTask = FryEggsAsync(2);
Task<Bacon> baconTask = FryBaconAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");
Console.WriteLine("Breakfast is ready!");
Ahora tiene un desayuno preparado de forma asincrónica que tarda unos 20 minutos en prepararse. El tiempo total de cocción se reduce porque algunas tareas se ejecutan simultáneamente.
El código actualiza el proceso de preparación reduciendo el tiempo de cocción, pero introducen una regresión al quemar los huevos y el bacon. Inicia todas las tareas asincrónicas a la vez. Solo espera en cada tarea cuando necesite los resultados. El código puede ser similar al programa de una aplicación web que realiza solicitudes a diferentes microservicios y, a continuación, combina los resultados en una sola página. Realiza todas las solicitudes inmediatamente y, a continuación, aplica la await
expresión en todas esas tareas y redacta la página web.
Apoyo a la composición con tareas
Las revisiones de código anteriores ayudan a preparar todo para el desayuno al mismo tiempo, excepto la tostada. El proceso de hacer la tostada es una composición de una operación asincrónica (tostar el pan) con operaciones sincrónicas (untar mantequilla y mermelada en la tostada). En este ejemplo se muestra un concepto importante sobre la programación asincrónica:
Importante
La composición de una operación asincrónica seguida del trabajo sincrónico es una operación asincrónica. Se indicó otra manera, si alguna parte de una operación es asincrónica, toda la operación es asincrónica.
En las actualizaciones anteriores, ha aprendido a usar objetos Task o Task<TResult> para contener tareas en ejecución. Espere a que termine cada tarea antes de usar su resultado. El siguiente paso es crear métodos que representen la combinación de otro trabajo. Antes de servir el desayuno, querrá esperar a completar la tarea que representa tostar el pan antes de untar la mantequilla y la mermelada.
Puede representar este trabajo con el código siguiente:
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
return toast;
}
El MakeToastWithButterAndJamAsync
método tiene el async
modificador en su firma, que indica al compilador que el método incluye una await
expresión y contiene operaciones asincrónicas. El método representa la tarea que tuesta el pan, luego extiende la mantequilla y la mermelada. El método devuelve un Task<TResult> objeto que representa la composición de las tres operaciones.
El bloque principal revisado de código ahora tiene el siguiente aspecto:
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
var eggs = await eggsTask;
Console.WriteLine("eggs are ready");
var bacon = await baconTask;
Console.WriteLine("bacon is ready");
var toast = await toastTask;
Console.WriteLine("toast is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
Este cambio de código ilustra una técnica importante para trabajar con código asincrónico. Puedes componer tareas separando las operaciones en un método nuevo que devuelva una tarea. Puede elegir cuándo esperar para continuar con esa tarea. Puede iniciar otras tareas simultáneamente.
Control de excepciones asincrónicas
Hasta este punto, el código asume implícitamente que todas las tareas se completan correctamente. Los métodos asincrónicos lanzan excepciones, al igual que sus homólogos sincrónicos. Los objetivos de compatibilidad asincrónica con excepciones y control de errores son los mismos que para la compatibilidad asincrónica en general. El procedimiento recomendado es escribir código que lea como una serie de instrucciones sincrónicas. Las tareas lanzan excepciones cuando no se pueden completar exitosamente. El código de cliente puede detectar esas excepciones cuando la await
expresión se aplica a una tarea iniciada.
En el ejemplo del desayuno, supongamos que la tostadora se activa mientras tosta el pan. Puede simular ese problema modificando el ToastBreadAsync
método para que coincida con el código siguiente:
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay(2000);
Console.WriteLine("Fire! Toast is ruined!");
throw new InvalidOperationException("The toaster is on fire");
await Task.Delay(1000);
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
Nota:
Al compilar este código, verá una advertencia sobre el código inaccesible. Se trata de un error por diseño. Después de que el tostador se active, las operaciones no continúan normalmente y el código devuelve un error.
Después de realizar los cambios en el código, ejecute la aplicación y compruebe la salida:
Pouring coffee
Coffee is ready
Warming the egg pan...
putting 3 slices of bacon in the pan
Cooking first side of bacon...
Putting a slice of bread in the toaster
Putting a slice of bread in the toaster
Start toasting...
Fire! Toast is ruined!
Flipping a slice of bacon
Flipping a slice of bacon
Flipping a slice of bacon
Cooking the second side of bacon...
Cracking 2 eggs
Cooking the eggs ...
Put bacon on plate
Put eggs on plate
Eggs are ready
Bacon is ready
Unhandled exception. System.InvalidOperationException: The toaster is on fire
at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in Program.cs:line 65
at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in Program.cs:line 36
at AsyncBreakfast.Program.Main(String[] args) in Program.cs:line 24
at AsyncBreakfast.Program.<Main>(String[] args)
Tenga en cuenta que bastantes tareas finalizan entre el momento en que el tostador se incendia y el sistema observa la excepción. Cuando una tarea que se ejecuta de forma asincrónica produce una excepción, se produce un error en esa tarea. El Task
objeto contiene la excepción lanzada en la propiedad Task.Exception. Las tareas con errores producen una excepción cuando la await
expresión se aplica a la tarea.
Hay dos mecanismos importantes para comprender este proceso:
- Cómo se almacena una excepción en una tarea con errores
- Cómo se desempaqueta una excepción y se vuelve a iniciar cuando el código espera (
await
) en una tarea con errores
Cuando el código que se ejecuta de forma asincrónica produce una excepción, la excepción se almacena en el Task
objeto . La propiedad Task.Exception es un objeto System.AggregateException porque pueden producirse más de una excepción durante el trabajo asincrónico. Cualquier excepción lanzada se agrega a la colección AggregateException.InnerExceptions. Si la Exception
propiedad es null, se crea un nuevo AggregateException
objeto y la excepción iniciada es el primer elemento de la colección.
El escenario más común para una tarea con errores es que la Exception
propiedad contiene exactamente una excepción. Cuando el código espera en una tarea con errores, vuelve a generar la primera AggregateException.InnerExceptions excepción de la colección. Este resultado es la razón por la que la salida del ejemplo muestra un System.InvalidOperationException objeto en lugar de un AggregateException
objeto. La extracción de la primera excepción interna hace que el trabajo con métodos asincrónicos sea lo más parecido posible a trabajar con sus homólogos sincrónicos. Puede examinar la propiedad Exception
en su código cuando su escenario podría generar varias excepciones.
Sugerencia
La práctica recomendada es que las excepciones de validación de argumentos surjan sincrónicamente de los métodos que devuelven tareas. Para obtener más información y ejemplos, vea Excepciones en métodos que devuelven tareas.
Antes de continuar con la siguiente sección, deja en comentarios las siguientes dos instrucciones dentro de tu método ToastBreadAsync
. No quieres iniciar otro incendio:
Console.WriteLine("Fire! Toast is ruined!");
throw new InvalidOperationException("The toaster is on fire");
Aplicar de manera eficiente las expresiones await a las tareas
Puede mejorar la serie de await
expresiones al final del código anterior mediante métodos de la Task
clase . Una API es el WhenAll método , que devuelve un Task objeto que se completa cuando se completan todas las tareas de su lista de argumentos. El código siguiente muestra este método:
await Task.WhenAll(eggsTask, baconTask, toastTask);
Console.WriteLine("Eggs are ready");
Console.WriteLine("Bacon is ready");
Console.WriteLine("Toast is ready");
Console.WriteLine("Breakfast is ready!");
Otra opción es usar el WhenAny método , que devuelve un Task<Task>
objeto que se completa cuando se completa cualquiera de sus argumentos. Puede esperar la tarea devuelta porque sabe que la tarea está terminada. En el código siguiente se muestra cómo puede usar el WhenAny método para esperar a que finalice la primera tarea y, a continuación, procesar su resultado. Después de procesar el resultado de la tarea completada, quite la tarea completada de la lista de tareas pasadas al WhenAny
método .
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("Eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("Bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("Toast is ready");
}
await finishedTask;
breakfastTasks.Remove(finishedTask);
}
Cerca del final del fragmento de código, observe la await finishedTask;
expresión. La expresión await Task.WhenAny
no espera a que la tarea finalice, sino que espera al objeto Task
devuelto por el método Task.WhenAny
. El resultado del Task.WhenAny
método es la tarea completada (o errónea). El procedimiento recomendado es volver a esperar con la tarea, incluso cuando sepas que la tarea está completa. De esta manera, puede recuperar el resultado de la tarea o asegurarse de que se produzca cualquier excepción que haga que se produzca un error en la tarea.
Revisión del código final
Este es el aspecto de la versión final del código:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("toast is ready");
}
await finishedTask;
breakfastTasks.Remove(finishedTask);
}
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
return toast;
}
private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");
private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay(3000);
Console.WriteLine("Remove toast from toaster");
return new Toast();
}
private static async Task<Bacon> FryBaconAsync(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
await Task.Delay(3000);
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
await Task.Delay(3000);
Console.WriteLine("Put bacon on plate");
return new Bacon();
}
private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay(3000);
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay(3000);
Console.WriteLine("Put eggs on plate");
return new Egg();
}
private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
}
}
El código completa las tareas de desayuno asincrónicas en unos 15 minutos. El tiempo total se reduce porque algunas tareas se ejecutan simultáneamente. El código supervisa simultáneamente varias tareas y realiza acciones solo según sea necesario.
El código final es asincrónico. Refleja con más precisión cómo una persona puede cocinar el desayuno. Compare el código final con el primer ejemplo de código del artículo. Las acciones principales siguen siendo claras leyendo el código. Puede leer el código final de la misma manera que leyó la lista de instrucciones para hacer un desayuno, como se muestra al principio del artículo. Las características del lenguaje para las palabras clave async
y await
proporcionan la traducción que cada persona realiza para seguir las instrucciones escritas: Inicia las tareas tan pronto como puedas y no te bloquees mientras esperas a que se completen.