Odpowiedzi SLaks i Killercam są dobre; Pomyślałem, że po prostu dodam trochę więcej kontekstu.
Twoje pierwsze pytanie dotyczy zasadniczo tego, jakie metody można oznaczyć async
.
Sposób oznaczone async
może powrócić void
, Task
albo Task<T>
. Jakie są między nimi różnice?
Task<T>
Powrocie sposób asynchroniczny może być oczekiwany, a po zakończeniu wykonywania zadania będzie to propozycja górę T.
ZA Task
Powrocie sposób asynchroniczny może być oczekiwany, a gdy finalizuje zadaniowe, kontynuacja zadania jest zaplanowane do uruchomienia.
void
Powrocie sposób asynchroniczny nie może być oczekiwany; jest to metoda „odpal i zapomnij”. Działa asynchronicznie i nie możesz powiedzieć, kiedy się skończy. To więcej niż trochę dziwne; jak mówi SLaks, normalnie zrobiłbyś to tylko podczas tworzenia asynchronicznej obsługi zdarzeń. Zdarzenie jest wyzwalane, program obsługi wykonuje; nikt nie będzie „czekał” na zadanie zwrócone przez moduł obsługi zdarzeń, ponieważ programy obsługi zdarzeń nie zwracają zadań, a nawet gdyby tak było, jaki kod użyłby zadania do czegoś? Zwykle to nie kod użytkownika przekazuje kontrolę do programu obsługi w pierwszej kolejności.
Twoje drugie pytanie w komentarzu dotyczy zasadniczo tego, co można await
edytować:
Jakie rodzaje metod można await
edytować? Czy można edytować metodę zwracającą pustkę await
?
Nie, nie można oczekiwać metody zwracającej pustkę. Kompilator tłumaczy await M()
na wywołanie funkcji M().GetAwaiter()
, gdzie GetAwaiter
może być metodą instancji lub metodą rozszerzającą. Oczekiwana wartość musi być wartością, za którą można dostać awaitera; oczywiście metoda zwracająca void nie tworzy wartości, z której można uzyskać awaiter.
Task
metody zwrotne mogą dawać oczekiwane wartości. Przewidujemy, że osoby trzecie będą chciały tworzyć własne implementacje Task
podobnych obiektów, na które można czekać, a Ty będziesz mógł na nie czekać. Jednak nie będzie można deklarować async
metod, które zwracają coś innego niż void
, Task
lub Task<T>
.
(AKTUALIZACJA: Moje ostatnie zdanie może zostać sfałszowane przez przyszłą wersję C #; istnieje propozycja zezwalająca na zwracanie typów innych niż typy zadań dla metod asynchronicznych).
(AKTUALIZACJA: wspomniana powyżej funkcja została wprowadzona do języka C # 7).
async void
metody zgłaszają wyjątek do tego,SynchronizationContext
który był aktywny w momencie rozpoczęcia wykonywania. Jest to podobne do zachowania (synchronicznych) programów obsługi zdarzeń. @DrewMarsh: plikUnobservedTaskException
and runtime mają zastosowanie tylko do metod zadań asynchronicznych „ i zapomnij” , a nie doasync void
metod.W przypadku, gdy dzwoniący chce poczekać na zadanie lub dodać kontynuację.
W rzeczywistości jedynym powodem powrotu
void
jest to, że nie możesz wrócić,Task
ponieważ piszesz procedurę obsługi zdarzeń.źródło
void
, nie masz możliwości przejścia do zadania, które generuje. (Właściwie nie jestem pewien, czy to w ogóle generujeTask
)Metody zwracają się
Task
iTask<T>
można je komponować - co oznacza, że możnaawait
je znaleźć wewnątrz plikuasync
metody.async
zwracające metodyvoid
nie są kompozowalne, ale mają dwie inne ważne właściwości:Druga kwestia jest ważna, gdy masz do czynienia z kontekstem, który utrzymuje liczbę oczekujących operacji asynchronicznych.
Kontekst ASP.NET jest jednym z takich kontekstów; jeśli używasz
Task
metod asynchronicznych bez oczekiwania na nie z asyncvoid
metody , żądanie ASP.NET zostanie ukończone zbyt wcześnie.Innym kontekstem jest ten,
AsyncContext
który napisałem dla testów jednostkowych (dostępny tutaj ) -AsyncContext.Run
metoda śledzi liczbę zaległych operacji i zwraca, gdy wynosi zero.źródło
Typ
Task<T>
jest typem konia roboczego biblioteki zadań równoległych (TPL), reprezentuje koncepcję „pewnej pracy / zadania, któreT
w przyszłości przyniesie wynik typu ”. Pojęcie „praca, która zakończy się w przyszłości, ale nie zwróci żadnego wyniku” jest reprezentowane przez nieogólny typ zadania.Dokładny sposób, w jaki zostanie utworzony wynik typu,
T
oraz szczegóły dotyczące realizacji określonego zadania; praca może zostać przeniesiona do innego procesu na komputerze lokalnym, do innego wątku itp. Zadania TPL są zwykle rozdzielane do wątków roboczych z puli wątków w bieżącym procesie, ale ten szczegół implementacji nie jest fundamentalny dlaTask<T>
typu; raczejTask<T>
może reprezentować dowolną operację o dużym opóźnieniu, która generuje plikT
.Na podstawie Twojego komentarza powyżej:
Te
await
środki wyrażenie „ocenić to wyrażenie, aby uzyskać obiekt reprezentujący prace, które zostaną w przyszłości produkować wynik. Zapisz się pozostałą część obecnej metody jako oddzwaniania związanego z kontynuacją tego zadania. Raz, że zadanie jest produkowany i tył wezwanie jest zarejestrowany, natychmiast zwróć kontrolę rozmówcy ”. Jest to przeciwieństwo / w przeciwieństwie do zwykłego wywołania metody, co oznacza „pamiętaj, co robisz, uruchamiaj tę metodę, aż zostanie całkowicie zakończona, a następnie kontynuuj od miejsca, w którym przerwałeś, teraz znając wynik metody”.Edycja: Powinienem zacytować artykuł Erica Lipperta z października 2011 r. MSDN Magazine, ponieważ był to dla mnie bardzo pomocny w zrozumieniu tych rzeczy.
Więcej informacji i białych stron można znaleźć tutaj .
Mam nadzieję, że to pomoże.
źródło