2012-09-09 5 views
72

Ich habe eine Zeit lang versucht, etwas zu bekommen, dachte ich würde einfach sein Arbeits .NET 4.5Run zwei Asynchron-Aufgaben parallel und sammle Ergebnisse in .NET 4.5

Ich möchte zwei lange laufenden Aufgaben abzufeuern zur gleichen Zeit und sammeln sie die
Ergebnisse in der besten C# 4.5 (RTM) Art und Weise

folgende funktioniert, aber ich mag es nicht, weil:

  • ich Sleep will so eine Asynchron-Methode sein, es kann await Oth er Methoden
  • Es sieht einfach ungeschickt mit Task.Run()
  • Ich glaube nicht, dass dies sogar überhaupt neue Sprachfunktionen verwendet!

Arbeitscode:

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

Nichtarbeitscode:

Update: Das funktioniert tatsächlich und ist der richtige Weg, es zu tun, das einzige Problem ist die Thread.Sleep

Dieser Code funktioniert nicht, weil der cal l bis Sleep(5000) startet die Task sofort, so dass Sleep(1000) nicht ausgeführt wird, bis sie abgeschlossen ist. Das ist richtig, obwohl Sleepasync ist und ich nicht await oder rufen .Result zu früh verwenden.

Ich dachte, vielleicht gibt es eine Möglichkeit, eine bekommen nicht laufenden Task<T> durch eine async Methode aufrufen, damit ich dann Start() auf die beiden Aufgaben nennen könnte, aber ich kann nicht herausfinden, wie man eine Task<T> bekommen von einem Asynchron-Aufruf Methode.

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

Anmerkung: Herstellung Go ein asynchrones Verfahren keinen Unterschied macht –

+3

-Block geschieht auf ' task1.Result 'nicht bei' var task1 = Sleep (5000) 'weil deine Sleep-Methode ohne await-Schlüsselwort ist synchron. – Arvis

Antwort

34

Sie sollten Task.Delay anstelle von Sleep für asynchrone Programmierung verwenden und dann Task.WhenAll verwenden, um die Aufgabenergebnisse zu kombinieren. Die Aufgaben würden parallel laufen.

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

Das ist eine gute Antwort ... aber ich dachte, das war falsch Antwort, bis ich es lief. dann habe ich verstanden. Es wird wirklich in 5 Sekunden ausgeführt. Der Trick besteht darin, die Aufgaben nicht sofort abzuwarten, sondern auf Task.WhenAll zu warten. –

3

Während Ihr Sleep Methode Asynchron ist, Thread.Sleep nicht. Die ganze Idee von async besteht darin, einen einzelnen Thread wiederzuverwenden und nicht mehrere Threads zu starten. Da Sie die Verwendung eines synchronen Aufrufs für Thread.Sleep blockiert haben, wird es nicht funktionieren.

Ich gehe davon aus, dass Thread.Sleep eine Vereinfachung dessen ist, was Sie eigentlich tun möchten. Kann Ihre tatsächliche Implementierung als asynchrone Methoden codiert werden?

Wenn Sie mehrere synchrone Blockierungsaufrufe ausführen müssen, suchen Sie woanders, denke ich!

+0

danke Richard - ja es scheint zu funktionieren, als ich tatsächlich meine Service-Anruf –

+0

dann verwenden, wie async laufen? Ich habe Anwendung, die viel Datei wechseln und auf Datei warten, etwa 5 Sekunden, und dann einen anderen Prozess, wenn ich "wenn für alle" zuerst zuerst ausgeführt wird, dann zweite, obwohl ich sagte: 'var x = y() ', und nicht' var x = erwarten y() 'oder' y(). wait() 'noch immer warten sie den ganzen Weg, und wenn async das nicht selbst behandelt, was soll ich tun? BEACHTEN SIE, dass y mit async dekoriert ist, und ich erwarte, dass es alles innerhalb der wenn alles tut, nicht direkt auf, wo es zugewiesen ist, EDIT: ich gerade als mein Partner sagte, lasst uns 'Task.Factory' versuchen, und er sagte es funktionierte, wenn ich Seite von dieser Klasse ausbringe – deadManN

1

Dieser Artikel half eine Menge Dinge zu erklären. Es ist im FAQ-Stil.

Async/Await FAQ

Dieser Teil erklärt, warum Thread.Sleep läuft auf dem gleichen ursprünglichen Thread - was zu meiner anfänglichen Verwirrung.

Hat das „Async“ keyword bewirken, dass der Aufruf einer Methode zur Thread Warteschlange? Um einen neuen Thread zu erstellen? Um ein Raketenschiff zu Mars zu starten?

Nr. Nr. Und Nr. Siehe die vorherigen Fragen. Das "async" Schlüsselwort zeigt dem Compiler an, dass "erwarten" innerhalb der Methode verwendet werden kann, so dass die Methode an einem Wartepunkt aussetzen kann und ihre Ausführung asynchron fortsetzen kann, wenn die erwartete Instanz abgeschlossen ist. Aus diesem Grund gibt der Compiler eine Warnung aus, wenn in einer Methode, die als "async" markiert ist, keine "wartet".

80
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 
} 
+1

I +1, weil Sie t1, t2 als Aufgabe deklarieren, was der richtige Weg ist. – Minime

+9

Ich glaube, dass diese Lösung erfordert, dass die Go-Methode auch asynchron ist, was bedeutet, dass sie asynchron ist. Wenn Sie etwas mehr wie den asker Fall wünschen, in dem die Go-Methode des Aufrufers synchron ist, aber zwei unabhängige Tasks asynchron ausführen möchte (dh keiner muss vor dem anderen abgeschlossen sein, aber beide müssen abgeschlossen sein, bevor die Ausführung fortgesetzt wird), dann Task .WaitAll' ** wäre besser, und Sie brauchen das "await" -Schlüsselwort nicht. Daher muss die aufrufende "Go" -Methode nicht selbst async sein. ** Kein Ansatz ist besser, es ist nur eine Frage dessen, was Ihr Ziel ist. ** – AaronLS

+1

Void-Methode: 'async void LongTask1() {...}' hat keine Task.Result-Eigenschaft. Verwenden Sie Task ohne T in einem solchen Fall: 'async Task LongTask1()'. – Arvis

0

Um diesen Punkt zu beantworten:

I Sleep will ein asynchrones Verfahren sein, so dass es andere Methoden, die Sie vielleicht die Sleep Funktion wie folgt umschreiben kann

erwarten können:

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äuft dieser Code ausgegeben:

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

Es ist Wochenende jetzt!

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

Ordinary Task.Factory Beispiel:

private static Task<int> 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 Beispiel:

private static Task<int> Sleep(int ms, string n) { 
    var tcs = new TaskCompletionSource<int>(); 
    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; 
}