Czy zaleca się używanie prevTask.Wait () z ContinueWith (z biblioteki Tasks)?

88

Niedawno powiedziano mi, że sposób, w jaki korzystam z mojego .ContinueWith for Tasks, nie jest właściwym sposobem ich używania. Nie znalazłem jeszcze dowodów na to w Internecie, więc zapytam was i zobaczę odpowiedź. Oto przykład, jak używam .ContinueWith:

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

Teraz wiem, że to prosty przykład i będzie działać bardzo szybko, ale załóżmy, że każde zadanie wykonuje dłuższą operację. Więc powiedziano mi, że w .ContinueWith musisz powiedzieć prevTask.Wait (); w przeciwnym razie możesz pracować przed zakończeniem poprzedniego zadania. Czy to w ogóle jest możliwe? Założyłem, że moje drugie i trzecie zadanie zostanie uruchomione dopiero po zakończeniu poprzedniego zadania.

Co mi powiedziano, jak napisać kod:

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
źródło
2
Nie używaj StartNew blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Chris Marisic

Odpowiedzi:

115

Echh… Myślę, że niektóre z obecnych odpowiedzi czegoś brakuje: co się dzieje z wyjątkami?

Jedynym powodem, dla którego wezwałbyś Waitkontynuację, byłoby zaobserwowanie potencjalnego wyjątku od poprzednika w samej kontynuacji. Ta sama obserwacja miałaby miejsce, gdybyś uzyskał dostęp Resultw przypadku a, Task<T>a także w przypadku ręcznego dostępu do Exceptionwłaściwości. Szczerze mówiąc, nie zadzwoniłbymWait ani nie uzyskiwał dostępu, Resultponieważ jeśli istnieje wyjątek, zapłacisz cenę ponownego podniesienia go, co jest niepotrzebnym narzutem. Zamiast tego możesz po prostu odznaczyć IsFaultedwłaściwość z poprzednika Task. Alternatywnie można tworzyć rozwidlone przepływy pracy, łącząc się w łańcuchy z wieloma rodzeńskimi kontynuacjami, które są uruchamiane tylko na podstawie sukcesu lub niepowodzenia z TaskContinuationOptions.OnlyOnRanToCompletioni TaskContinuationOptions.OnlyOnFaulted.

Teraz nie jest konieczne obserwowanie wyjątku poprzednika w kontynuacji, ale możesz nie chcieć, aby przepływ pracy posuwał się naprzód, jeśli, powiedzmy, „Krok 1” nie powiódł się. W tym przypadku: podając TaskContinuationOptions.NotOnFaulteddo ContinueWithpołączeń uniemożliwiłyby logikę kontynuacyjny ze nigdy nawet wypalania.

Pamiętaj, że jeśli twoje własne kontynuacje nie obserwują wyjątku, osoba, która czeka na zakończenie tego ogólnego przepływu pracy, będzie tą osobą, która go obserwuje. Albo są Waitna Taskwyższym poziomie, albo zdecydowali się na własną kontynuację, aby wiedzieć, kiedy jest ukończona. Jeśli jest to drugie, ich kontynuacja wymagałaby zastosowania wspomnianej logiki obserwacji.

Drew Marsh
źródło
2
W końcu ktoś udzieli poprawnej odpowiedzi. @ Travyguy9 Przeczytaj tę odpowiedź @DrewMarsh i przeczytaj więcej na tematTaskContinuationOptions
Jasper
2
Świetna odpowiedź, szukałem: „Pamiętaj, że jeśli twoje własne kontynuacje nie obserwują wyjątku, osoba, która czeka na zakończenie tego ogólnego przepływu pracy, będzie to obserwować”. Jedno pytanie, kiedy twoje zadanie nie jest czekane, kto jest domyślnym kelnerem? (Nie udało się znaleźć odpowiedzi)
Thibault D.
20

Używasz go poprawnie.

Tworzy kontynuację, która jest wykonywana asynchronicznie po zakończeniu zadania docelowego.

Źródło: metoda Task.ContinueWith (akcja jako MSDN)

Konieczność wywoływania prevTask.Wait()każdego Task.ContinueWithwywołania wydaje się dziwnym sposobem powtarzania niepotrzebnej logiki - tj. Robienia czegoś, aby być „super duper pewnym”, ponieważ tak naprawdę nie rozumiesz, co robi określony fragment kodu. Na przykład sprawdzanie wartości zerowej tylko po to, aby rzucić miejsce, w ArgumentNullExceptionktórym i tak zostałoby rzucone.

Więc nie, ktokolwiek ci powiedział, że jest zły i prawdopodobnie nie rozumie, dlaczego Task.ContinueWithistnieje.

Anders Arpi
źródło
16

Kto Ci to powiedział?

Cytując MSDN :

Tworzy kontynuację, która jest wykonywana asynchronicznie po zakończeniu zadania docelowego.

Jaki byłby cel kontynuowania, gdyby nie czekał na zakończenie poprzedniego zadania?

Możesz nawet przetestować to samodzielnie:

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
źródło
5

Z MSDN naTask.Continuewith

Zwrócone zadanie nie zostanie zaplanowane do wykonania, dopóki bieżące zadanie nie zostanie zakończone. Jeśli kryteria określone za pomocą parametru continuationOptions nie są spełnione, zadanie kontynuacji zostanie anulowane zamiast zaplanowane.

Myślę, że sposób, w jaki spodziewasz się, że zadziała w pierwszym przykładzie, jest właściwy.

mclark1129
źródło
2

Możesz również rozważyć użycie Task.Run zamiast Task.Factory.StartNew.

Wpis na blogu Stephena Cleary'ego i post Stephena Toub , do którego się odwołuje, wyjaśniają różnice. W tej odpowiedzi jest również dyskusja .

bizcad
źródło
4
Negocjowany, ponieważ nie odnosi się do rzeczywistego pytania. Dodaje trochę wartości, ale powinien być komentarzem.
Sinaesthetic
0

Uzyskując dostęp Task.Result, wykonujesz podobną logikę dotask.wait

Sameh
źródło
Tak. Możemy uniknąć metody Wait (). Ale działa tylko z zadaniami wynikowymi, np. Zadanie <bool>
Alexander Ulmaskulov
Negocjowany, ponieważ nie odnosi się do rzeczywistego pytania. Dodaje trochę wartości, ale powinien być komentarzem.
Sinaesthetic
0

Powtórzę to, co wielu już mówiło, prevTask.Wait() jest niepotrzebne .

Aby uzyskać więcej przykładów, można przejść do Chaining Tasks using Continuation Tasks , jeszcze jedno łącze firmy Microsoft z dobrymi przykładami.

Amit Dash
źródło