web-dev-qa-db-fra.com

Task.Factory.StartNew avec async lambda et Task.WaitAll

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?

14
Jacob Roberts

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 () => ...
16
Charles Mager

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);
18
Yuval Itzchakov

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...
        }
    }
0
hebinda