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

J’ai essayé pendant un certain temps d’obtenir quelque chose que je pensais être simple en travaillant avec .NET 4.5

Je veux lancer deux longues tâches en même temps et collecter les
donne le meilleur résultat C # 4.5 (RTM)

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

  • Je veux que le Sleep soit une méthode asynchrone pour qu’il puisse await autres méthodes
  • Il semble simplement maladroit avec Task.Run()
  • Je ne pense pas que cela utilise même 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 est la manière correcte 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 pour que Sleep(1000) ne s’exécute pas jusqu’à ce qu’il soit terminé. Cela est vrai même si Sleep est async et que je n’utilise pas .Result ou .Result trop tôt.

J’ai pensé qu’il y avait peut-être un moyen d’obtenir une Task non- async en appelant une méthode async pour pouvoir ensuite appeler Start() sur les deux tâches, mais je n’arrive pas à comprendre comment obtenir une Task 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 Sleep(int ms) { Console.WriteLine("Sleeping for " + ms); Thread.Sleep(ms); return ms; } 

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

 public class Program { static void Main(ssortingng[] 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 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; } } 
 async Task LongTask1() { ... } async Task LongTask2() { ... } ... { Task t1 = LongTask1(); Task t2 = LongTask2(); await Task.WhenAll(t1,t2); //now we have t1.Result and t2.Result } 

Pendant que votre méthode de Sleep est asynchrone, Thread.Sleep ne l’est pas. L’async consiste à réutiliser un seul thread et non à lancer plusieurs threads. Parce que vous avez bloqué en utilisant un appel synchrone à Thread.Sleep, ça 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 de blocage synchrones, regardez ailleurs, je pense!

C’est le weekend maintenant!

 public static void Go() { Console.WriteLine("Start fosterage...\n"); var t1 = Sleep(5000, "Kevin"); var t2 = Sleep(3000, "Jerry"); var result = Task.WhenAll(t1, t2).Result; Console.WriteLine("\nMy precious spare time last for only {0}ms", result.Max()); Console.WriteLine("Press any key and take same beer..."); Console.ReadKey(); } private static async Task Sleep(int ms, ssortingng n) { Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); await Task.Delay(ms); Console.WriteLine("{0} waked up after {1}ms :(", n, ms); return ms; } 

Exemple ordinaire de Task.Factory :

  private static Task Sleep(int ms, ssortingng n) { return Task.Factory.StartNew(() => { Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); Thread.Sleep(ms); Console.WriteLine("{0} waked up after {1}ms :(", n, ms); return ms; }); } 

Exemple mystérieux de TaskCompletionSource :

 private static Task Sleep(int ms, ssortingng n) { var tcs = new TaskCompletionSource(); Console.WriteLine("{0} going to sleep for {1}ms :)", n, ms); var t = Task.Factory.StartNew(() => { Thread.Sleep(ms); Console.WriteLine("{0} waked up after {1}ms :(", n, ms); tcs.SetResult(ms); }); return tcs.Task; } 

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

Async / Attente FAQ

Cette partie explique pourquoi Thread.Sleep s’exécute sur le même thread d’origine – menant à ma confusion initiale.

Le mot clé «async» provoque-t-il l’appel d’une méthode à mettre en queue sur le 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 puisse être suspendue à un point d’attente et que son exécution reprenne de manière asynchrone à la fin de l’instance attendue. C’est pourquoi le compilateur émet un avertissement s’il n’y a pas de «attente» à l’intérieur d’une méthode marquée comme «asynchrone».

Pour répondre à ce point:

Je veux que le sumil soit une méthode asynchrone pour qu’il puisse attendre d’autres méthodes

Vous pouvez peut-être réécrire la fonction Sleep comme ceci:

 private static async Task 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(ssortingng[] 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 produira:

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