WaitAll vs WhenAll

332

Was ist der Unterschied zwischen Task.WaitAll()und Task.WhenAll()vom Async CTP? Können Sie einen Beispielcode bereitstellen, um die verschiedenen Anwendungsfälle zu veranschaulichen?

Yaron Levi
quelle

Antworten:

502

Task.WaitAll blockiert den aktuellen Thread, bis alles abgeschlossen ist.

Task.WhenAllGibt eine Aufgabe zurück, die die Aktion des Wartens darstellt, bis alles abgeschlossen ist.

Das bedeutet, dass Sie mit einer asynchronen Methode Folgendes verwenden können:

await Task.WhenAll(tasks);

... was bedeutet, dass Ihre Methode fortgesetzt wird, wenn alles abgeschlossen ist, aber Sie werden bis dahin keinen Thread binden, um nur herumzuhängen.

Jon Skeet
quelle
2
Nach langem
Vince Panuccio
7
@Vince: Ich denke, "nichts mit Threads zu tun" ist eine Übertreibung, und es ist wichtig zu verstehen, wie asynchrone Operationen mit Threads interagieren.
Jon Skeet
6
@ KevinBui: Nein, es sollte es nicht blockieren - es wartet auf die von zurückgegebene Aufgabe WhenAll, aber das ist nicht dasselbe wie das Blockieren des Threads.
Jon Skeet
1
@ JonSkeet Vielleicht ist mir die genaue Unterscheidung zwischen diesen beiden zu subtil. Können Sie mich (und möglicherweise den Rest von uns) auf eine Referenz hinweisen, die den Unterschied deutlich macht?
CatShoes
124
@CatShoes: Nicht wirklich - ich habe es so gut wie möglich erklärt. Ich denke, ich könnte eine Analogie geben - es ist wie der Unterschied zwischen der Bestellung eines Imbisses und dem Stehen an der Tür, um darauf zu warten, dass es ankommt, oder der Bestellung eines Imbisses, anderen Dingen und dem Öffnen der Tür, wenn der Kurier ankommt ...
Jon Skeet
50

Während die Antwort von JonSkeet den Unterschied auf typisch hervorragende Weise erklärt, gibt es einen weiteren Unterschied: die Ausnahmebehandlung .

Task.WaitAlllöst eine aus, AggregateExceptionwenn eine der Aufgaben ausgelöst wird, und Sie können alle ausgelösten Ausnahmen untersuchen. Das awaitin await Task.WhenAllpackt das aus AggregateExceptionund gibt nur die erste Ausnahme zurück.

Wenn das folgende Programm mit await Task.WhenAll(taskArray)der Ausgabe ausgeführt wird, ist dies wie folgt.

19/11/2016 12:18:37 AM: Task 1 started
19/11/2016 12:18:37 AM: Task 3 started
19/11/2016 12:18:37 AM: Task 2 started
Caught Exception in Main at 19/11/2016 12:18:40 AM: Task 1 throwing at 19/11/2016 12:18:38 AM
Done.

Wenn das folgende Programm mit Task.WaitAll(taskArray)der Ausgabe ausgeführt wird, ist wie folgt.

19/11/2016 12:19:29 AM: Task 1 started
19/11/2016 12:19:29 AM: Task 2 started
19/11/2016 12:19:29 AM: Task 3 started
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 1 throwing at 19/11/2016 12:19:30 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 2 throwing at 19/11/2016 12:19:31 AM
Caught AggregateException in Main at 19/11/2016 12:19:32 AM: Task 3 throwing at 19/11/2016 12:19:32 AM
Done.

Das Programm:

class MyAmazingProgram
{
    public class CustomException : Exception
    {
        public CustomException(String message) : base(message)
        { }
    }

    static void WaitAndThrow(int id, int waitInMs)
    {
        Console.WriteLine($"{DateTime.UtcNow}: Task {id} started");

        Thread.Sleep(waitInMs);
        throw new CustomException($"Task {id} throwing at {DateTime.UtcNow}");
    }

    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            await MyAmazingMethodAsync();
        }).Wait();

    }

    static async Task MyAmazingMethodAsync()
    {
        try
        {
            Task[] taskArray = { Task.Factory.StartNew(() => WaitAndThrow(1, 1000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(2, 2000)),
                                 Task.Factory.StartNew(() => WaitAndThrow(3, 3000)) };

            Task.WaitAll(taskArray);
            //await Task.WhenAll(taskArray);
            Console.WriteLine("This isn't going to happen");
        }
        catch (AggregateException ex)
        {
            foreach (var inner in ex.InnerExceptions)
            {
                Console.WriteLine($"Caught AggregateException in Main at {DateTime.UtcNow}: " + inner.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught Exception in Main at {DateTime.UtcNow}: " + ex.Message);
        }
        Console.WriteLine("Done.");
        Console.ReadLine();
    }
}
Tymtam
quelle
13
Der größte praktische Unterschied ist die Ausnahmebehandlung. "Ja wirklich?" Denn das ist wirklich nicht der größte praktische Unterschied. Der größte praktische Unterschied besteht darin, dass einer asynchron und nicht blockierend ist, während der andere blockiert. Dies ist viel wichtiger als der Umgang mit Ausnahmen.
Liam
5
Vielen Dank für den Hinweis. Diese Erklärung war in dem Szenario, in dem ich gerade arbeite, hilfreich. Vielleicht nicht der "größte praktische Unterschied", aber definitiv ein guter Ruf.
Urk
Die Ausnahmebehandlung, die den größten praktischen Unterschied darstellt, könnte für den Vergleich zwischen await t1; await t2; await t3;vsawait Task.WhenAll(t1,t2,t3);
frostshoxx am
1
Widerspricht dieses Ausnahmeverhalten nicht den Dokumenten hier ( docs.microsoft.com/en-us/dotnet/api/… ) ? "Wenn eine der bereitgestellten Aufgaben in einem fehlerhaften Zustand ausgeführt wird, wird die zurückgegebene Aufgabe auch in einem fehlerhaften Zustand ausgeführt , wobei seine Ausnahmen die Aggregation der nicht ausgepackten Ausnahmen von jeder der bereitgestellten Aufgaben enthalten. "
Dasith Wijes
1
Ich betrachte dies als ein Artefakt awaitund nicht als einen Unterschied zwischen den beiden Methoden. Beide verbreiten einen AggregateException, entweder direkt oder durch eine Eigenschaft (die Task.ExceptionEigenschaft).
Theodor Zoulias
20

Als Beispiel für den Unterschied: Wenn Sie eine Aufgabe haben, führt dies etwas mit dem UI-Thread aus (z. B. einer Aufgabe, die eine Animation in einem Storyboard darstellt), wenn Sie Task.WaitAll()dann den UI-Thread blockieren und die UI nie aktualisiert wird. Wenn Sie verwenden, await Task.WhenAll()wird der UI-Thread nicht blockiert und die UI wird aktualisiert.

J. Long
quelle
7

Was machen Sie:

  • Intern machen beide dasselbe.

Was ist der Unterschied:

  • WaitAll ist ein blockierender Anruf
  • WhenAll - not - Code wird weiterhin ausgeführt

Verwenden Sie welche, wenn:

  • WaitAll wann kann nicht fortgesetzt werden, ohne das Ergebnis zu haben
  • WhenAll wann was gerade benachrichtigt werden soll, nicht gesperrt
i100
quelle
1
@MartinRhodes Aber was ist, wenn Sie nicht sofort darauf warten, sondern mit einer anderen Arbeit fortfahren und dann darauf warten? Sie haben diese Möglichkeit nicht so, WaitAllwie ich es verstehe.
Jeppe
@Jeppe Würdest du den Anruf nicht einfach anders machen, Task.WaitAll nachdem du deine andere Arbeit erledigt hast? Ich meine, anstatt es gleich nach dem Start Ihrer Aufgaben aufzurufen.
PL