without - programmation asynchrone c#




Utiliser async/await pour plusieurs tâches (3)

J'étais curieux de voir les résultats des méthodes fournies dans la question ainsi que la réponse acceptée, alors je l'ai mis à l'épreuve.

Voici le code:

class Program
{
    class Worker
    {
        public int Id { get; set; }
        public int SleepTimeout { get; set; }

        public async Task DoWork()
        {
            Console.WriteLine("Worker {0} started on thread {1} at {2}.",
                Id, Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("hh:mm:ss.fff"));
            await Task.Run(() => Thread.Sleep(SleepTimeout));
            Console.WriteLine("Worker {0} stopped at {1}.",
                Id, DateTime.Now.ToString("hh:mm:ss.fff"));
        }
    }

    static void Main(string[] args)
    {
        var workers = new List<Worker>
        {
            new Worker { Id = 1, SleepTimeout = 3000 },
            new Worker { Id = 2, SleepTimeout = 3000 },
            new Worker { Id = 3, SleepTimeout = 3000 },
            new Worker { Id = 4, SleepTimeout = 3000 },
            new Worker { Id = 5, SleepTimeout = 3000 },
        };

        Console.WriteLine("Starting test: Parallel.ForEach");
        PerformTest_ParallelForEach(workers);
        Console.WriteLine("Test finished.\n");

        Console.WriteLine("Starting test: Task.WaitAll");
        PerformTest_TaskWaitAll(workers);
        Console.WriteLine("Test finished.\n");

        Console.WriteLine("Starting test: Task.WhenAll");
        var task = PerformTest_TaskWhenAll(workers);
        task.Wait();
        Console.WriteLine("Test finished.\n");

        Console.ReadKey();
    }

    static void PerformTest_ParallelForEach(List<Worker> workers)
    {
        Parallel.ForEach(workers, worker => worker.DoWork().Wait());
    }

    static void PerformTest_TaskWaitAll(List<Worker> workers)
    {
        Task.WaitAll(workers.Select(worker => worker.DoWork()).ToArray());
    }

    static Task PerformTest_TaskWhenAll(List<Worker> workers)
    {
        return Task.WhenAll(workers.Select(worker => worker.DoWork()));
    }
}

Et la sortie résultante:

J'utilise un client API complètement asynchrone, c'est-à-dire que chaque opération renvoie la Task ou la Task<T> , par exemple:

static async Task DoSomething(int siteId, int postId, IBlogClient client)
{
    await client.DeletePost(siteId, postId); // call API client
    Console.WriteLine("Deleted post {0}.", siteId);
}

En utilisant les opérateurs asynchrones / en attente C # 5, quelle est la manière correcte / la plus efficace de démarrer plusieurs tâches et d'attendre qu'elles soient toutes terminées:

int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());

ou:

int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());

Comme le client API utilise HttpClient en interne, je m'attendrais à ce qu'il émette 5 requêtes HTTP immédiatement, en écrivant sur la console à la fin de chaque session.


Puisque l'API que vous appelez est asynchrone, la version de Parallel.ForEach n'a pas beaucoup de sens. Vous ne devriez pas utiliser .Wait dans la version WaitAll car cela perdrait le parallélisme. Une autre alternative si l'appelant est async utilise Task.WhenAll après avoir fait Select et ToArray pour générer le tableau des tâches. Une deuxième alternative utilise Rx 2.0


int[] ids = new[] { 1, 2, 3, 4, 5 };
Parallel.ForEach(ids, i => DoSomething(1, i, blogClient).Wait());

Bien que vous exécutiez les opérations en parallèle avec le code ci-dessus, ce code bloque chaque thread sur lequel s'exécute chaque opération. Par exemple, si l'appel réseau prend 2 secondes, chaque thread se bloque pendant 2 secondes sans rien faire d'autre que d'attendre.

int[] ids = new[] { 1, 2, 3, 4, 5 };
Task.WaitAll(ids.Select(i => DoSomething(1, i, blogClient)).ToArray());

D'autre part, le code ci-dessus avec WaitAll bloque également les threads et vos threads ne seront pas libres de traiter tout autre travail jusqu'à la fin de l'opération.

Approche recommandée

Je préférerais que WhenAll qui effectuera vos opérations de manière asynchrone en parallèle.

public async Task DoWork() {

    int[] ids = new[] { 1, 2, 3, 4, 5 };
    await Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
}

En fait, dans le cas ci-dessus, vous n'avez même pas besoin d' await , vous pouvez simplement revenir directement de la méthode car vous n'avez pas de suites:

public Task DoWork() 
{
    int[] ids = new[] { 1, 2, 3, 4, 5 };
    return Task.WhenAll(ids.Select(i => DoSomething(1, i, blogClient)));
}

Pour étayer ceci, voici un article de blog détaillé sur toutes les alternatives et leurs avantages / inconvénients: Comment et où les E / S asynchrones simultanées avec l'API Web ASP.NET





c#-5.0