Czy ktoś może wyjaśnić, czy await
i ContinueWith
są synonimami, czy nie w poniższym przykładzie. Próbuję po raz pierwszy skorzystać z TPL i przeczytałem całą dokumentację, ale nie rozumiem różnicy.
Oczekuj :
String webText = await getWebPage(uri);
await parseData(webText);
Kontynuuj z :
Task<String> webText = new Task<String>(() => getWebPage(uri));
Task continue = webText.ContinueWith((task) => parseData(task.Result));
webText.Start();
continue.Wait();
Czy jeden jest preferowany w określonych sytuacjach?
c#
task-parallel-library
task
async-await
Harrison
źródło
źródło
Wait
połączenia w drugim przykładzie następnie dwa fragmenty będzie (w większości) równoważne.getWebPage
metoda nie może być używana w obu kodach. W pierwszym kodzie maTask<string>
typ zwracany, w drugimstring
typ zwracany. więc w zasadzie twój kod się nie kompiluje. - żeby być precyzyjnym.Odpowiedzi:
W drugim kodzie synchronicznie czekasz na zakończenie kontynuacji. W pierwszej wersji metoda powróci do obiektu wywołującego, gdy tylko trafi w pierwsze
await
wyrażenie, które nie jest jeszcze zakończone.Są bardzo podobne pod tym względem, że obaj planują kontynuację, ale gdy tylko przepływ sterowania stanie się nawet nieco skomplikowany,
await
prowadzi do znacznie prostszego kodu. Dodatkowo, jak zauważył Servy w komentarzach, oczekiwanie na zadanie spowoduje „rozpakowanie” zagregowanych wyjątków, co zwykle prowadzi do prostszej obsługi błędów. Użycie równieżawait
niejawnie zaplanuje kontynuację w kontekście wywołania (chyba że używaszConfigureAwait
). To nic, czego nie można zrobić „ręcznie”, ale o wiele łatwiej to zrobićawait
.Sugeruję, abyś spróbował zaimplementować nieco większą sekwencję operacji z obydwoma
await
iTask.ContinueWith
- może to być prawdziwy otwieracz oczu.źródło
await
nadContinueWith
w tym względzie.parseData
wykonywany.Oto sekwencja fragmentów kodu, których ostatnio użyłem, aby zilustrować różnicę i różne problemy przy użyciu rozwiązań asynchronicznych.
Załóżmy, że masz program obsługi zdarzeń w aplikacji opartej na GUI, który zajmuje dużo czasu, więc chciałbyś, aby był asynchroniczny. Oto logika synchroniczna, od której zaczynasz:
LoadNextItem zwraca zadanie, które ostatecznie da wynik, który chcesz sprawdzić. Jeśli bieżący wynik jest tym, którego szukasz, aktualizujesz wartość jakiegoś licznika w interfejsie użytkownika i wracasz z metody. W przeciwnym razie kontynuujesz przetwarzanie większej liczby elementów z LoadNextItem.
Pierwszy pomysł na wersję asynchroniczną: po prostu użyj kontynuacji! I na razie zignorujmy część zapętloną. Mam na myśli, co mogłoby się nie udać?
Świetnie, teraz mamy metodę, która nie blokuje! Zamiast tego ulega awarii. Wszelkie aktualizacje formantów interfejsu użytkownika powinny mieć miejsce w wątku interfejsu użytkownika, więc musisz to uwzględnić. Na szczęście istnieje opcja określenia, w jaki sposób powinny być zaplanowane kontynuacje, i jest domyślna tylko do tego:
Świetnie, teraz mamy metodę, która się nie zawiesza! Zamiast tego po cichu zawodzi. Kontynuacje same w sobie są oddzielnymi zadaniami, których status nie jest powiązany ze statusem zadania poprzedzającego. Więc nawet jeśli LoadNextItem zakończy się niepowodzeniem, wywołujący zobaczy tylko zadanie, które zostało pomyślnie zakończone. OK, po prostu przekaż wyjątek, jeśli taki istnieje:
Świetnie, teraz to faktycznie działa. Dla jednej pozycji. A co z tym zapętleniem. Okazuje się, że rozwiązanie odpowiadające logice oryginalnej wersji synchronicznej będzie wyglądało mniej więcej tak:
Lub, zamiast wszystkich powyższych, możesz użyć async, aby zrobić to samo:
Teraz jest o wiele ładniej, prawda?
źródło