J'essaie d'utiliser Task.WaitAll
sur une liste de tâches. Le problème, c’est que les tâches sont un lambda asynchrone qui casse Tasks.WaitAll
car il n’attend jamais.
Voici un exemple de bloc de code:
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
Cela n'attend pas à cause de l'async lambda. Alors, comment suis-je censé attendre les opérations d'E/S dans mon lambda?
Task.Factory.StartNew
ne reconnaît pas les délégués async
car il n'y a pas de surcharge qui accepte une fonction renvoyant un Task
.
Ceci plus d'autres raisons (voir StartNew is dangerous ) est la raison pour laquelle vous devriez utiliser Task.Run
ici:
tasks.Add(Task.Run(async () => ...
Cela n'attend pas à cause de l'async lambda. Alors, comment suis-je supposé Attendre les opérations d’E/S dans mon lambda?
La raison pour laquelle Task.WaitAll
n'attend pas l'achèvement du travail IO présenté par votre async lambda est parce que Task.Factory.StartNew
renvoie en fait un Task<Task>
. Puisque votre liste est un List<Task>
(et que Task<T>
provient de Task
), vous attendez la tâche externe lancée par StartNew
, tout en ignorant la liste interne créée par async lambda. C'est pourquoi ils disent que Task.Factory.StartNew
est dangerous en ce qui concerne async.
Comment pourriez-vous résoudre ce problème? Vous pouvez appeler explicitement Task<Task>.Unwrap()
pour obtenir la tâche interne:
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());
Ou, comme d’autres l’ont dit, vous pouvez appeler Task.Run
à la place:
tasks.Add(Task.Run(async () => /* lambda */);
De plus, puisque vous voulez bien faire les choses, vous voudrez utiliser Task.WhenAll
, pourquoi est-on asynchrone à attendre, au lieu de Task.WaitAll
qui bloque de manière synchrone:
await Task.WhenAll(tasks);
Vous pouvez faire comme ça.
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...
}
}