web-dev-qa-db-fra.com

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

Cela faisait un moment que j'essayais d'obtenir quelque chose qui me paraissait simple avec .NET 4.5

Je veux lancer deux tâches longues en même temps et récupérer le
donne les meilleurs résultats en C # 4.5 (RTM)

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

  • Je veux que Sleep soit une méthode asynchrone pour pouvoir await d'autres méthodes
  • Ça a l'air maladroit avec Task.Run()
  • Je ne pense même pas que cela utilise de nouvelles fonctionnalités linguistiques!

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 réellement et c'est la bonne façon de le faire, le seul problème est le Thread.Sleep

Ce code ne fonctionne pas car l'appel à Sleep(5000) démarre immédiatement la tâche en cours d'exécution afin que Sleep(1000) ne s'exécute pas tant qu'elle n'est pas terminée. Ceci est vrai même si Sleep est async et que je n’utilise ni await ni n’appelle .Result Trop tôt.

Je pensais qu'il était peut-être possible d'obtenir un Task<T> Non en cours d'exécution en appelant une méthode async afin que je puisse ensuite appeler Start() sur les deux tâches, mais je peux " t comprendre comment obtenir un Task<T> en appelant une méthode async.

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;
}
106
Simon_Weaver

Vous devez utiliser Task.Delay au lieu de Sleep pour la programmation async, puis utiliser Task.WhenAll pour combiner les résultats de la tâche. Les tâches se dérouleraient 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;
        }
    }
73
softveda
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
}
106
Bart

Alors que votre méthode Sleep est asynchrone, Thread.Sleep n'est pas. L'idée asynchrone est de réutiliser un seul thread et non de démarrer plusieurs threads. Parce que vous avez bloqué l'utilisation d'un appel synchrone à Thread.Sleep, cela ne fonctionnera pas.

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éthode asynchrone?

Si vous devez exécuter plusieurs appels bloquants synchrones, cherchez ailleurs, je pense!

3
Richard

Pour répondre à ce point:

Je veux que Sleep soit une méthode asynchrone pour pouvoir attendre d'autres méthodes

vous pouvez peut-être réécrire la fonction 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 générera:

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

C'est week-end maintenant!

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }
2
Arvis

Malheureusement, si vous avez une autre attente dans vos méthodes, Task.WhenAll () ne vous aidera pas. Les méthodes asynchrones ne sont pas parallèles.

Pour une exécution parallèle réelle, vous devez démarrer de nouvelles tâches manuellement, par exemple. using Task.Run () ou ConfigureAwait (false).

Regardez ici pour plus de détails: https://www.wintellect.com/tasks-are-still-not-threads-and-async-is-not-parallel/

0
bside

Cet article a aidé à expliquer beaucoup de choses. C'est dans le style FAQ.

FAQ Async/En attente

Cette partie explique pourquoi Thread.Sleep fonctionne sur le même fil d’origine, ce qui a provoqué ma confusion initiale.

Le mot clé "async" provoque-t-il l'appel d'une méthode à la file d'attente du ThreadPool? Pour créer un nouveau fil? Lancer une fusée sur Mars?

Non. Et non. Voir les questions précédentes. Le mot clé "async" indique au compilateur que "wait" peut être utilisé à l'intérieur de la méthode, de sorte que la méthode peut être suspendue à un point d'attente et que son exécution reprend de manière asynchrone à la fin de l'instance attendue. C’est pourquoi le compilateur émet un avertissement s’il n’ya pas d’attente à l’intérieur d’une méthode marquée "async".

0
Simon_Weaver