Wird empfohlen, prevTask.Wait () mit ContinueWith (aus der Tasks-Bibliothek) zu verwenden?

88

So wurde mir kürzlich gesagt, dass die Verwendung von .ContinueWith for Tasks nicht der richtige Weg ist, sie zu verwenden. Ich habe noch keine Beweise dafür im Internet gefunden, also werde ich euch fragen und sehen, wie die Antwort lautet. Hier ist ein Beispiel für die Verwendung von .ContinueWith:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
}

Jetzt weiß ich, dass dies ein einfaches Beispiel ist und sehr schnell ausgeführt wird, aber nehmen Sie einfach an, dass jede Aufgabe einen längeren Vorgang ausführt. Mir wurde also gesagt, dass Sie in .ContinueWith prevTask.Wait () sagen müssen; Andernfalls können Sie arbeiten, bevor die vorherige Aufgabe abgeschlossen ist. Ist das überhaupt möglich? Ich ging davon aus, dass meine zweite und dritte Aufgabe erst ausgeführt wird, wenn die vorherige Aufgabe abgeschlossen ist.

Was mir gesagt wurde, wie man den Code schreibt:

public Task DoSomething()
{
    return Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 2");
    })
    .ContinueWith((prevTask) =>
    {
        prevTask.Wait();
        Console.WriteLine("Step 3");
    });
}
Travyguy9
quelle
2
Verwenden Sie nicht StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Chris Marisic

Antworten:

115

Ehhh .... Ich denke, einigen der aktuellen Antworten fehlt etwas: Was passiert mit Ausnahmen?

Der einzige Grund, warum Sie Waiteine Fortsetzung aufrufen würden, wäre die Beobachtung einer möglichen Ausnahme vom Vorgänger in der Fortsetzung selbst. Die gleiche Beobachtung würde passieren, wenn Sie Resultim Fall von a Task<T>zugreifen und auch wenn Sie manuell auf die ExceptionEigenschaft zugreifen. Ehrlich gesagt würde ich nicht anrufen Waitoder darauf zugreifen, Resultdenn wenn es eine Ausnahme gibt, zahlen Sie den Preis für die erneute Erhöhung, was unnötigen Aufwand bedeutet. Stattdessen können Sie die IsFaultedEigenschaft einfach aus dem Vorgänger abhaken Task. Alternativ können Sie gegabelte Workflows erstellen, indem Sie mehrere Geschwisterfortsetzungen verketten, die nur aufgrund von Erfolg oder Misserfolg mit TaskContinuationOptions.OnlyOnRanToCompletionund ausgelöst werden TaskContinuationOptions.OnlyOnFaulted.

Jetzt ist es nicht erforderlich, die Ausnahme des Antezedens in der Fortsetzung zu beachten, aber Sie möchten möglicherweise nicht, dass Ihr Workflow fortgesetzt wird, wenn beispielsweise "Schritt 1" fehlgeschlagen ist. In diesem Fall: Wenn Sie TaskContinuationOptions.NotOnFaultedIhre ContinueWithAnrufe angeben, wird verhindert, dass die Fortsetzungslogik jemals ausgelöst wird.

Denken Sie daran, dass die Person, die darauf wartet, dass dieser gesamte Workflow abgeschlossen wird, diejenige ist, die ihn beobachtet, wenn Ihre eigenen Fortsetzungen die Ausnahme nicht beachten. Entweder sind sie Waitim TaskUpstream oder sie haben ihre eigene Fortsetzung angepackt, um zu wissen, wann sie abgeschlossen ist. Wenn es das letztere ist, müsste ihre Fortsetzung die oben erwähnte Beobachtungslogik verwenden.

Drew Marsh
quelle
2
Endlich gibt jemand eine richtige Antwort. @ Travyguy9 Bitte lesen Sie diese @ DrewMarsh Antwort und lesen Sie mehr überTaskContinuationOptions
Jasper
2
Tolle Antwort, ich suchte nach "Denken Sie daran, dass, wenn Ihre eigenen Fortsetzungen die Ausnahme nicht beachten, die Person, die darauf wartet, dass dieser gesamte Workflow abgeschlossen wird, diejenige sein wird, die ihn beobachtet." Eine Frage: Wenn Ihre Aufgabe nicht gewartet wird, wer ist der Standardkellner? (Konnte die Antwort darauf nicht finden)
Thibault D.
20

Sie verwenden es richtig.

Erstellt eine Fortsetzung, die asynchron ausgeführt wird, wenn die Zielaufgabe abgeschlossen ist.

Quelle: Task.ContinueWith-Methode (Aktion als MSDN)

prevTask.Wait()Jeden Task.ContinueWithAufruf aufrufen zu müssen, scheint eine seltsame Art zu sein, unnötige Logik zu wiederholen - dh etwas zu tun, um "super duper sicher" zu sein, weil man eigentlich nicht versteht, was ein bestimmtes Stück Code bewirkt. Als würde man nach einer Null suchen, nur um eine zu werfen, ArgumentNullExceptionwo sie sowieso geworfen worden wäre.

Also, nein, wer auch immer dir gesagt hat, dass das falsch ist und wahrscheinlich nicht versteht, warum es Task.ContinueWithexistiert.

Anders Arpi
quelle
16

Wer hat dir das gesagt?

MSDN zitieren :

Erstellt eine Fortsetzung, die asynchron ausgeführt wird, wenn die Zielaufgabe abgeschlossen ist.

Was wäre der Zweck von Continue With, wenn nicht darauf gewartet würde, dass die vorherige Aufgabe abgeschlossen wird?

Sie können es sogar selbst testen:

Task.Factory.StartNew(() =>
    {
        Console.WriteLine("Step 1");
        Thread.Sleep(2000);
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("I waited step 1 to be completed!");
    })
    .ContinueWith((prevTask) =>
    {
        Console.WriteLine("Step 3");
    });
ken2k
quelle
5

Aus der MSDN aufTask.Continuewith

Die zurückgegebene Aufgabe wird erst dann ausgeführt, wenn die aktuelle Aufgabe abgeschlossen ist. Wenn die im Parameter continuationOptions angegebenen Kriterien nicht erfüllt sind, wird die Fortsetzungsaufgabe abgebrochen und nicht geplant.

Ich denke, dass die Art und Weise, wie Sie es im ersten Beispiel erwarten, die richtige ist.

mclark1129
quelle
2

Möglicherweise möchten Sie auch Task.Run anstelle von Task.Factory.StartNew verwenden.

Stephen Clearys Blog-Post und der von ihm referenzierte Stephen Toub- Post erklären die Unterschiede. In dieser Antwort gibt es auch eine Diskussion .

bizcad
quelle
4
Downvoted, weil es die eigentliche Frage nicht anspricht. Es fügt etwas Wert hinzu, sollte aber ein Kommentar sein.
Sinaesthetic
0

Mit Access machen Task.ResultSie tatsächlich eine ähnliche Logik wietask.wait

Sameh
quelle
Ja. Wir können die Wait () -Methode vermeiden. Aber es funktioniert nur mit Ergebnisaufgaben, zB Aufgabe <bool>
Alexander Ulmaskulov
Downvoted, weil es die eigentliche Frage nicht anspricht. Es fügt etwas Wert hinzu, sollte aber ein Kommentar sein.
Sinaesthetic