140 votes

Exécuter deux tâches asynchrones en parallèle et collecter les résultats en .NET 4.5

Cela fait un moment que j'essaie de faire fonctionner quelque chose que je croyais simple avec .NET 4.5.

Je veux lancer deux tâches à long terme en même temps et collecter les données de l'enquête.
résultats dans la meilleure façon C# 4.5 (RTM)

Ce qui suit fonctionne mais je ne l'aime pas parce que.. :

  • Je veux Sleep pour être une méthode asynchrone afin qu'elle puisse await autres méthodes
  • Cela semble juste maladroit avec Task.Run()
  • Je ne pense même pas que cela utilise de nouvelles fonctionnalités du langage !

Code de travail :

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

Code non fonctionnel :

Mise à jour : Cela fonctionne en fait et c'est la bonne façon de faire, le seul problème est le Thread.Sleep

Ce code ne fonctionne pas car l'appel à Sleep(5000) lance immédiatement la tâche en cours d'exécution de sorte que Sleep(1000) ne s'exécute pas tant qu'elle n'est pas terminée. Ceci est vrai même si Sleep es async et je n'utilise pas await ou en appelant .Result trop tôt.

J'ai pensé qu'il y avait peut-être un moyen de faire en sorte qu'une personne qui ne fonctionne pas Task<T> en appelant un async pour que je puisse ensuite appeler Start() sur les deux tâches, mais je n'arrive pas à trouver comment obtenir une Task<T> d'appeler une méthode asynchrone.

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

140voto

Bart Points 564
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

103voto

Pratik Points 5401

Vous devriez utiliser Task.Delay au lieu de Sleep pour la programmation asynchrone, puis utiliser Task.WhenAll pour combiner les résultats des tâches. Les tâches s'exécuteraient en parallèle.

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

3voto

Richard Points 8614

Alors que votre Sleep est asynchrone, Thread.Sleep ne l'est pas. L'idée de l'asynchronisme est de réutiliser un seul fil, et non de lancer plusieurs fils. Parce que vous avez bloqué en utilisant un appel synchrone à Thread.Sleep, cela ne va pas fonctionner.

Je suppose que Thread.Sleep est une simplification de ce que vous voulez réellement faire. Votre implémentation réelle peut-elle être codée en tant que méthodes asynchrones ?

Si vous avez besoin d'exécuter plusieurs appels synchrones bloquants, regardez ailleurs je pense !

2voto

Hans Passant Points 475940

Je ne suis pas sûr de ce dont il s'agit, le cas d'utilisation est un peu trop synthétique. Mais vous séquencez les tâches simplement en utilisant l'opérateur await plus d'une fois. L'équivalent de votre code qui utilise pleinement le motif ressemblerait à ceci :

    private void button1_Click(object sender, EventArgs e)
    {
        Go();
    }

   public async static void Go()
    {
        Console.WriteLine("Starting");

        var result1 = await Sleep(5000); 
        var result2 = await Sleep(1000);

        int totalSlept = result1 + result2;

        Console.WriteLine("Slept for " + totalSlept + " ms");
    }

    private static async Task<int> Sleep(int ms)
    {
        Console.WriteLine("Sleeping for " + ms);
        await Task.Delay(ms);
        return ms;
    }

2voto

asidis Points 1

Pour répondre à ce point :

Je veux que Sleep soit une méthode asynchrone pour qu'elle puisse attendre d'autres méthodes.

vous pouvez peut-être réécrire le Sleep comme ceci :

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

l'exécution de ce code donnera comme résultat :

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X