EDITAR: Actualización LongTask() para await Task.Delay(2000); hace que funcione como lo necesito, ¡así que gracias por las respuestas, a todos!
Estoy tratando de llamar a una función asíncrona desde dentro de una función de sincronización para permitir que la función de sincronización continúe antes de que finalice la función asíncrona. No sé por qué el siguiente código espera que LongTask() se complete antes de continuar. Pensé que solo debería hacer eso si await LongTask() . ¿Puede alguien ayudarme a entender lo que me estoy perdiendo?
C# violín aquí
using System; using System.Diagnostics; using System.Threading.Tasks; public class Program { public static void Main() { var timer = new Stopwatch(); timer.Start(); LongTask(); timer.Stop(); long ms = timer.ElapsedMilliseconds; Console.WriteLine(ms.ToString()); } async static void LongTask() { Task.Delay(2000).Wait(); } }Como ya se mencionó en los comentarios:
debe usar await Task.Delay(2000) para decirle al compilador que desea ejecutar Task.Delay(2000) de forma asíncrona. Si observa más de cerca el ejemplo proporcionado por usted, el compilador muestra una advertencia:
Este método asincrónico carece de operadores 'await' y se ejecutará sincrónicamente [...]
Con suerte, esto lo demostrará mejor:
static async Task Main(string[] args) { var timer = Stopwatch.StartNew(); var t1 = LongTask(); var t2 = LongTask(); Console.WriteLine("Started both tasks in {0}", timer.Elapsed); await Task.WhenAll(t1, t2); timer.Stop(); Console.WriteLine("Tasks finished in {0}", timer.Elapsed); } async static Task LongTask() { await Task.Delay(2000); } Observe el cambio en los tipos de retorno de las funciones. Observe también la salida. Ambas tareas están esperando algo (en este caso, la demora en expirar) en paralelo y, al mismo tiempo, la función Main puede continuar ejecutándose, imprimiendo texto y luego esperando el resultado de esas funciones.
Es importante comprender que la ejecución de una función asíncrona se ejecuta de forma síncrona hasta que llega al operador de await . Si no tiene ninguna await en una función asíncrona, no se ejecutará de forma asíncrona (y el compilador debería advertirlo). Esto se aplica de forma recursiva (si tiene varias esperas anidadas) hasta que llega a algo que realmente se va a esperar. Ver ejemplo:
static async Task Main(string[] args) { var timer = Stopwatch.StartNew(); var t1 = LongTask(1); Console.WriteLine("t1 started in {0}", timer.Elapsed); var t2 = LongTask(2); Console.WriteLine("Started both tasks in {0}", timer.Elapsed); await Task.WhenAll(t1, t2); timer.Stop(); Console.WriteLine("Tasks finished in {0}", timer.Elapsed); } async static Task LongTask(int id) { Console.WriteLine("[{0}] Before subtask 1", id); await LongSubTask1(); Console.WriteLine("[{0}] Before subtask 2", id); await LongSubTask2(); Console.WriteLine("[{0}] After subtask 1", id); } async static Task LongSubTask1(int id) { Console.WriteLine("[{0}] LongSubTask1", id); await Task.Delay(1000); } async static Task LongSubTask2(int id) { Console.WriteLine("[{0}] LongSubTask2", id); await Task.Delay(1000); } Si lo ejecuta, verá que la ejecución se ejecuta sincrónicamente hasta la llamada Task.Delay y solo entonces regresa a la función Main .
Para beneficiarse de async/await, debe usarlo hasta el punto en que termine llamando a alguna E/S (red, disco, etc.) que deberá esperar (o algún otro evento que no requieren un sondeo constante para averiguarlo, o creados artificialmente como un retraso en esa muestra). Si necesita realizar un cálculo costoso (que no tiene nada asíncrono) sin ocupar un subproceso en particular (por ejemplo, subproceso de interfaz de usuario en la aplicación GUI), deberá iniciar explícitamente una nueva tarea con él, que en algún momento posterior puede await para obtener su resultado.