c# - net - Task.WaitTodo no esperando que se complete la tarea
task c# example (4)
Aquí, Task.WaitAll
espera la tarea externa y no la interna. Use Task.Run
para no tener tareas anidadas. Esa es la mejor solución de prácticas. Otra solución es esperar también la tarea interna. Por ejemplo:
Task<object> testTask = Task.Factory.StartNew(
async (obj) =>
{
...
}
).Unwrap();
O:
testTask.Wait();
testTask.Result.Wait();
Mientras trataba de descubrir la nueva (tal vez no tan nueva ahora, pero nueva para mí, de todos modos) Programación asincrónica de Task
en C #, me encontré con un problema que me llevó un poco descubrir, y no estoy seguro de por qué.
He solucionado el problema, pero todavía no estoy seguro de por qué fue un problema para empezar. Solo pensé en compartir mi experiencia en caso de que alguien se encuentre con la misma situación.
Si algún gurú quisiera informarme de la causa del problema, sería maravilloso y muy apreciado. ¡Siempre me gusta saber por qué algo no funciona!
Hice una tarea de prueba, de la siguiente manera:
Random rng = new Random((int)DateTime.UtcNow.Ticks);
int delay = rng.Next(1500, 15000);
Task<Task<object>> testTask = Task.Factory.StartNew<Task<object>>(
async (obj) =>
{
DateTime startTime = DateTime.Now;
Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (int)obj);
await Task.Delay((int)obj);
Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds);
return obj;
},
delay
);
Task<Task<object>>[] tasks = new Task<Task<object>>[] { testTask };
Task.WaitAll(tasks);
Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff"));
// make console stay open till user presses enter
Console.ReadLine();
Y luego ejecuté la aplicación para ver qué escupía. Aquí hay algunos resultados de muestra:
6: 06: 15.5661 - Iniciando la tarea de prueba con un retraso de 3053 ms.
6: 06: 15.5662 - Terminada la espera.
6: 06: 18.5743 - La tarea de prueba finalizó después de 3063.235ms.
Como puede ver, Task.WaitAll(tasks);
declaración no hizo mucho. Esperaba un gran total de 1 milisegundo antes de continuar la ejecución.
He respondido mi propia "pregunta" a continuación, pero como dije antes, si alguien más conocedor de lo que me gustaría explicarle por qué no funciona, ¡por favor! ( Creo que podría tener algo que ver con la ejecución ''paso'' del método una vez que llegue a un operador en await
, y luego volver a entrar una vez que la espera esté lista ... Pero probablemente estoy equivocado)
Debería evitar el uso de Task.Factory.StartNew
con async- Task.Factory.StartNew
. Deberías usar Task.Run
en Task.Run
lugar.
Un método asíncrono devuelve una Task<T>
, un delegado asíncrono también. Task.Factory.StartNew
también devuelve una Task<T>
, donde su resultado es el resultado del parámetro de delegado. Entonces, cuando se usan juntos, devuelve una Task<Task<T>>>
.
Todo lo que esta Task<Task<T>>
hace es ejecutar el delegado hasta que haya una tarea por devolver, que es cuando se alcanza la primera espera. Si solo espera a que se complete esa tarea, no está esperando el método completo, solo la parte antes de la primera espera.
Puede solucionarlo utilizando Task.Unwrap
que crea una Task<T>
que representa esa Task<Task<T>>>
:
Task<Task> wrapperTask = Task.Factory.StartNew(...);
Task actualTask = wrapperTask.Unwrap();
Task.WaitAll(actualTask);
Después de muchas vueltas y tirones de pelo finalmente decidí deshacerme de la lambda asincrónica y simplemente usar el método System.Threading.Thread.Sleep
, para ver si eso hacía alguna diferencia.
El nuevo código terminó así:
Random rng = new Random((int)DateTime.UtcNow.Ticks);
int delay = rng.Next(1500, 15000);
Task<object> testTask = Task.Factory.StartNew<object>(
(obj) =>
{
DateTime startTime = DateTime.Now;
Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (int)obj);
System.Threading.Thread.Sleep((int)obj);
Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds);
return obj;
},
delay
);
Task<object>[] tasks = new Task<object>[] { testTask };
Task.WaitAll(tasks);
Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff"));
// make console stay open till user presses enter
Console.ReadLine();
Nota: Debido a la eliminación de la palabra clave async
del método lambda, el tipo de tarea ahora puede ser simplemente Task<object>
lugar de Task<Task<object>>
- puede ver este cambio en el código anterior.
¡Y voilá! ¡Funcionó! Tengo el ''Acabado esperando''. mensaje DESPUÉS de completar la tarea.
Por desgracia, recuerdo haber leído en alguna parte que no deberías usar System.Threading.Thread.Sleep()
en tu código de Task
. No puedo recordar por qué; pero como era solo para probar y la mayoría de las tareas realmente harán algo que lleva tiempo, en lugar de pretender hacer algo que lleva tiempo , esto no debería ser realmente un problema.
Espero que esto ayude a algunas personas. Definitivamente no soy el mejor programador del mundo (o incluso cercano) y mi código probablemente no sea excelente, pero si ayuda a alguien, ¡genial! :)
Gracias por leer.
Editar: Las otras respuestas a mi pregunta explican por qué tuve el problema que hice y esta respuesta solo resolvió el problema por error. Cambiando a Thread.Sleep (x) no tuvo efecto . ¡Gracias a todas las personas que me respondieron y me ayudaron!
El problema con su código es que hay dos tareas en juego. Uno es el resultado de su llamada Task.Factory.StartNew
, que hace que la función anónima se ejecute en el grupo de subprocesos. Sin embargo, su función anónima se compila a su vez para producir una tarea anidada , que representa la finalización de sus operaciones asincrónicas. Cuando espera en su Task<Task<object>>
, solo está esperando en la tarea externa . Para esperar en la tarea interna, debe usar Task.Run
lugar de Task.Factory.StartNew
, ya que desenvuelve automáticamente su tarea interna:
Random rng = new Random((int)DateTime.UtcNow.Ticks);
int delay = rng.Next(1500, 15000);
Task<int> testTask = Task.Run(
async () =>
{
DateTime startTime = DateTime.Now;
Console.WriteLine("{0} - Starting test task with delay of {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), delay);
await Task.Delay(delay);
Console.WriteLine("{0} - Test task finished after {1}ms.", DateTime.Now.ToString("h:mm:ss.ffff"), (DateTime.Now - startTime).TotalMilliseconds);
return delay;
});
Task<int>[] tasks = new[] { testTask };
Task.WaitAll(tasks);
Console.WriteLine("{0} - Finished waiting.", DateTime.Now.ToString("h:mm:ss.ffff"));
// make console stay open till user presses enter
Console.ReadLine();