2016-04-06 3 views
4

Ich habe noch eine Lernphase mit C# durchlaufen und bin auf eine Frage gestoßen, mit der ich Hilfe brauchte. Unter Berücksichtigung des folgenden Codes:Refactoring Async/Warten auf Parallelverarbeitung

private async Task<String> PrintTask() 
{ 
    await Task.Delay(3000); 
    return "Hello"; 
} 

private async void SayHelloTwice() 
{ 
    string firstHello = await PrintTask(); 
    string secondHello = await PrintTask(); 

    Console.WriteLine(firstHello); 
    Console.WriteLine(secondHello); 
} 

Im Moment wird SayHelloTwice() 6 Sekunden dauern, um abzuschließen. Ich möchte jedoch, dass die Retrieval-Aufgaben parallel ausgeführt werden, so dass es nur 3 Sekunden dauert, um abzuschließen. Wie würde ich meinen Code umgestalten, um das zu erreichen? Vielen Dank!

+1

Nur ein Kommentar über die Terminologie: "parallel" mehrere Threads impliziert. Was Sie wollen, ist asynchron * Nebenläufigkeit *, nicht * Parallelität *. –

Antwort

4

Der richtige Weg, dies (ohne Gefahr von Deadlocks) zu tun ist, Task.WhenAll

private async void SayHelloTwice() 
{ 
    string[] results = await Task.WhenAll(
     PrintTask(), 
     PrintTask()); 

    Console.WriteLine(results[0]); 
    Console.WriteLine(results[1]); 
} 
verwenden

Oft Bibliothek Autoren wird sich bemühen, den „Rückruf“ Code in einer Aufgabe auf dem ursprünglichen Thread ausgeführt zu machen, die aufgerufen es. Dies liegt oft daran, dass auf Objekte nur von einem einzigen Thread aus zugegriffen werden kann.

Bei Verwendung der blockierenden Aufrufe wie Task.Result und Task.WaitAll wird der Thread ausgesetzt (kann keine zusätzliche Arbeit leisten), bis die Task abgeschlossen ist.

Wie bereits erwähnt, wird oft die Task auf den aufrufenden Thread warten, um frei zu werden, so kann es verwendet werden, um die Aufgabe abzuschließen.

Im ersten Fall hält der äußere "Task" den Thread und wartet auf den Abschluss.

Der zweite Fall, der innere "Task" wartet auf den Thread zu beenden.

Ergo wird auch nie vollständig ausgeführt werden.

+0

Wenn Sie die Gefahr von "Deadlocks" einbringen, können Sie auch erklären, wann und wo dies ein Problem ist. – NPSF3000

+0

@ NPSF3000 Ich hatte bereits auf eine gelöschte Antwort .... Gib mir eine Sekunde ... – Aron

+2

Diese Antwort ist richtig Ich fühle mich wie die ganze Deadlock-Erklärung ist überflüssig, da es nichts mit der eigentlichen Frage zu tun hat – Itsik

3

Im Allgemeinen wollen Sie beide Aufgaben starten und danach warten. Eine einfache Art und Weise ist:

private async void SayHelloTwice() 
{ 
    // Start the tasks, don't wait 
    Task<string> firstHello = PrintTask(); 
    Task<string> secondHello = PrintTask(); 

    // Wait for results 
    string firstResult = await firstHello; 
    string secondResult = await secondHello; 

    // Print results 
    Console.WriteLine(firstResult); 
    Console.WriteLine(secondResult); 
} 

Was hier passiert, ist der Aufruf PrintTask() Durchführung des Verfahrens beginnen und sobald die Ausführung des ersten await erreicht, die einen tatsächlichen async Betrieb der Fall ist, wird die laufende Task zurückgegeben werden und zugewiesen zum ersten Hallo. Das gleiche gilt für secondHello. Dann warten Sie auf beide, um das Ergebnis zu vervollständigen und zu drucken.

Diese Implementierung ist nur ein Beispiel, um die Funktionsweise zu vereinfachen. In der realen Welt Code sollten Sie wahrscheinlich die Task.WhenAll verwenden für alle laufenden Aufgaben warten

+0

Wie unterscheidet sich das von OP? –

+1

Beide Aufgaben starten zur gleichen Zeit, im Gegensatz zu warten auf die erste Aufgabe zu beenden, bevor Sie die zweite – Itsik

+0

Ah ich sehe das in den Kommentaren jetzt. Vielen Dank. –

3
  • Sie Task.WhenAll verwenden können mehrere Aufgaben zu warten.
  • Sie sollten auch ein Task Rückkehr lieber, dass void (async/await - when to return a Task vs void?):

    private static async Task SayHelloTwice() 
    { 
        var hellos = await Task.WhenAll(PrintTask(), PrintTask()); 
        Console.WriteLine(hellos[0]); 
        Console.WriteLine(hellos[1]); 
    }