Estoy tratando de usar Task.WaitAll en una lista de tareas. La cuestión es que las tareas son una lambda asíncrona que rompe Tasks.WaitAll ya que nunca espera.
Aquí hay un bloque de código de ejemplo:
List<Task> tasks = new List<Task>(); tasks.Add(Task.Factory.StartNew(async () => { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } } Task.WaitAll(tasks); //do more stuff hereEsto no espera debido a la lambda asíncrona. Entonces, ¿cómo se supone que debo esperar las operaciones de E/S en mi lambda?
Task.Factory.StartNew no reconoce los delegados async , ya que no hay una sobrecarga que acepte una función que devuelve una Task .
Esta y otras razones (ver StartNew es peligroso ) es la razón por la que debería usar Task.Run aquí:
tasks.Add(Task.Run(async () => ...Esto no espera debido a la lambda asíncrona. Entonces, ¿cómo se supone que debo esperar las operaciones de E/S en mi lambda?
El motivo por el que Task.WaitAll no espera a que finalice el trabajo de E/S presentado por su lambda asíncrona es porque Task.Factory.StartNew en realidad devuelve una Task<Task> . Dado que su lista es List<Task> (y Task<T> se deriva de Task ), espera en la tarea externa iniciada por StartNew , mientras ignora la interna creada por async lambda. Es por eso que dicen que Task.Factory.StartNew es peligroso con respecto a async.
¿Cómo pudiste arreglar esto? Podría llamar explícitamente a Task<Task>.Unwrap() para obtener la tarea interna:
List<Task> tasks = new List<Task>(); tasks.Add(Task.Factory.StartNew(async () => { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } }).Unwrap()); O como dijeron otros, podrías llamar a Task.Run en su lugar:
tasks.Add(Task.Run(async () => /* lambda */); Además, dado que desea hacer las cosas bien, querrá usar Task.WhenAll , por qué se puede esperar de forma asíncrona, en lugar de Task.WaitAll que bloquea de forma síncrona:
await Task.WhenAll(tasks);Puedes hacer esto.
void Something() { List<Task> tasks = new List<Task>(); tasks.Add(ReadAsync()); Task.WaitAll(tasks.ToArray()); } async Task ReadAsync() { using (dbContext = new DatabaseContext()) { var records = await dbContext.Where(r => r.Id = 100).ToListAsync(); //do long cpu process here... } }