Esegui due task asincroni in parallelo e raccogli i risultati in .NET 4.5

Ho cercato per un po ‘di ottenere qualcosa che pensavo sarebbe stato semplice lavorare con .NET 4.5

Voglio licenziare due compiti di lunga durata allo stesso tempo e raccogliere il
si traduce nel modo migliore C # 4.5 (RTM)

I seguenti lavori, ma non mi piace perché:

  • Voglio che Sleep sia un metodo asincrono in modo che possa await altri metodi
  • Sembra goffo con Task.Run()
  • Non penso nemmeno che stia usando le nuove funzionalità linguistiche!

Codice di lavoro:

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

Codice non funzionante:

Aggiornamento: Questo funziona davvero ed è il modo corretto per farlo, l’unico problema è il Thread.Sleep

Questo codice non funziona perché la chiamata a Sleep(5000) avvia immediatamente l’attività in esecuzione in modo che Sleep(1000) non venga eseguito fino al completamento. Questo è vero anche se Sleep è async e non sto usando .Result o .Result . .Result troppo presto.

Ho pensato che forse c’è un modo per ottenere un’attività non in esecuzione Task chiamando un metodo async modo da poter quindi chiamare Start() nelle due attività, ma non riesco a capire come ottenere un’attività Task dalla chiamata di un metodo asincrono.

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

È necessario utilizzare Task.Delay anziché Sleep per la programmazione asincrona e quindi utilizzare Task.WhenAll per combinare i risultati dell’attività. I compiti sarebbero eseguiti in parallelo.

 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 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 } 

Mentre il metodo di Thread.Sleep è asincrono, Thread.Sleep non lo è. L’intera idea di async è di riutilizzare un singolo thread, non di avviare più thread. Poiché hai bloccato l’utilizzo di una chiamata sincrona a Thread.Sleep, non funzionerà.

Thread.Sleep che Thread.Sleep sia una semplificazione di ciò che in realtà vuoi fare. La tua implementazione effettiva può essere codificata come metodo asincrono?

Se hai bisogno di eseguire più chiamate di blocco sincrone, guarda altrove, penso!

È un fine settimana ora!

 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, string 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; } 

Esempio ordinario . Esempio di fabbrica :

  private static Task Sleep(int ms, string 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; }); } 

Mysterious TaskCompletionSource example:

 private static Task Sleep(int ms, string 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; } 

Questo articolo ha aiutato a spiegare molte cose. È in stile FAQ.

Async / Await FAQ

Questa parte spiega perché Thread.Sleep viene eseguito sullo stesso thread originale, causando la mia confusione iniziale.

La parola chiave “async” causa l’invocazione di un metodo per accodarsi al ThreadPool? Per creare un nuovo thread? Per lanciare una nave spaziale su Marte?

No. No. E no. Vedi le domande precedenti. La parola chiave “async” indica al compilatore che “await” può essere usato all’interno del metodo, in modo tale che il metodo possa sospendere in un punto di attesa e riprendere la sua esecuzione in modo asincrono al termine dell’istanza attesa. Questo è il motivo per cui il compilatore emette un avviso se non ci sono “attese” all’interno di un metodo contrassegnato come “asincrono”.

Per rispondere a questo punto:

Voglio che Sleep sia un metodo asincrono in modo che possa attendere altri metodi

puoi forse riscrivere la funzione Sleep questo modo:

 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(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’esecuzione di questo codice produrrà:

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