web-dev-qa-db-fra.com

Comment utiliser wait dans une boucle

J'essaie de créer une application de console asynchrone qui effectue un peu de travail sur une collection. J'ai une version qui utilise parallèle pour boucler une autre version qui utilise async/wait. Je m'attendais à ce que la version async/wait fonctionne de manière similaire à la version parallèle, mais elle s'exécute de manière synchrone. Qu'est-ce que je fais mal?

class Program
{
    static void Main(string[] args)
    {
        var worker = new Worker();
        worker.ParallelInit();
        var t = worker.Init();
        t.Wait();
        Console.ReadKey();
    }
}

public class Worker
{
    public async Task<bool> Init()
    {
        var series = Enumerable.Range(1, 5).ToList();
        foreach (var i in series)
        {
            Console.WriteLine("Starting Process {0}", i);
            var result = await DoWorkAsync(i);
            if (result)
            {
                Console.WriteLine("Ending Process {0}", i);
            }
        }

        return true;
    }

    public async Task<bool> DoWorkAsync(int i)
    {
        Console.WriteLine("working..{0}", i);
        await Task.Delay(1000);
        return true;
    }

    public bool ParallelInit()
    {
        var series = Enumerable.Range(1, 5).ToList();
        Parallel.ForEach(series, i =>
        {
            Console.WriteLine("Starting Process {0}", i);
            DoWorkAsync(i);
            Console.WriteLine("Ending Process {0}", i);
        });
        return true;
    }
}
74
Satish

La façon dont vous utilisez le mot clé await indique à C # que vous souhaitez attendre à chaque fois que vous passez dans la boucle, qui n'est pas parallèle. Vous pouvez réécrire votre méthode comme celle-ci pour faire ce que vous voulez, en stockant une liste de Tasks, puis de awaitles tous avec Task.WhenAll .

public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5).ToList();
    var tasks = new List<Task<Tuple<int, bool>>>();
    foreach (var i in series)
    {
        Console.WriteLine("Starting Process {0}", i);
        tasks.Add(DoWorkAsync(i));
    }
    foreach (var task in await Task.WhenAll(tasks))
    {
        if (task.Item2)
        {
            Console.WriteLine("Ending Process {0}", task.Item1);
        }
    }
    return true;
}

public async Task<Tuple<int, bool>> DoWorkAsync(int i)
{
    Console.WriteLine("working..{0}", i);
    await Task.Delay(1000);
    return Tuple.Create(i, true);
}
101
Tim S.

Votre code attend que chaque opération (en utilisant await) se termine avant de commencer l'itération suivante.
Par conséquent, vous n’obtenez aucun parallélisme.

Si vous souhaitez exécuter une opération asynchrone existante en parallèle, vous n'avez pas besoin de await; il vous suffit d'obtenir une collection de Tasks et d'appeler Task.WhenAll() pour renvoyer une tâche qui les attend toutes:

return Task.WhenAll(list.Select(DoWorkAsync));
32
SLaks
public async Task<bool> Init()
{
    var series = Enumerable.Range(1, 5);
    Task.WhenAll(series.Select(i => DoWorkAsync(i)));
    return true;
}
9
Vladimir