c# - parallel - Task.Factory.StartNew con async lambda y Task.WaitAll
task parallel library c# (4)
Estoy tratando de usar
Task.WaitAll
en una lista de tareas.
La
Tasks.WaitAll
es que las tareas son una lambda asíncrona que rompe las
Tasks.WaitAll
. Espera
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 here
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?
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?
La razón por la que
Task.WaitAll
no espera la finalización del trabajo de E / S presentado por su lambda asíncrona es porque
Task.Factory.StartNew
realidad devuelve una
Task<Task>
.
Dado que su lista es una
List<Task>
(y la
Task<T>
deriva de la
Task
), espera en la tarea externa iniciada por
StartNew
, mientras
ignora la interna
creada por el lambda asíncrono.
Es por eso que dicen
Task.Factory.StartNew
es
peligroso
con respecto a async.
¿Cómo puedes arreglar esto?
Podría llamar explícitamente a la
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ía llamar a
Task.Run
en
Task.Run
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 asincrónicamente, en lugar de
Task.WaitAll
que bloquea sincrónicamente:
await Task.WhenAll(tasks);
Puedes hacer así.
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...
}
}
tienes que usar el método
Task.ContinueWith
.
Me gusta esto
List<Task> tasks = new List<Task>();
tasks.Add(Task.Factory.StartNew(() =>
{
using (dbContext = new DatabaseContext())
{
return dbContext.Where(r => r.Id = 100).ToListAsync().ContinueWith(t =>
{
var records = t.Result;
// do long cpu process here...
});
}
}
}
Task.Factory.StartNew
no reconoce a los delegados
async
ya que no hay sobrecarga que acepte una función que devuelve una
Task
.
Esta más otras razones (ver
StartNew es peligroso
) es la razón por la que deberías usar
Task.Run
aquí:
tasks.Add(Task.Run(async () => ...