Moja metoda powraca Task. Chcę poczekać, aż to się skończy. Czego powinienem użyć
.Wait()lub .GetAwaiter().GetResult()? Jaka jest różnica między nimi?
Moja metoda powraca Task. Chcę poczekać, aż to się skończy. Czego powinienem użyć
.Wait()lub .GetAwaiter().GetResult()? Jaka jest różnica między nimi?
Odpowiedzi:
Oba są synchronicznym oczekiwaniem na wynik operacji (i należy ich unikać, jeśli to możliwe).
Różnica polega głównie na obsłudze wyjątków. W Waitprzypadku ślad stosu wyjątków jest niezmieniony i reprezentuje rzeczywisty stos w momencie wystąpienia wyjątku, więc jeśli masz fragment kodu działający w wątku puli wątków, masz stos taki jak
ThreadPoolThread.RunTask
YourCode.SomeWork
Z drugiej strony, .GetAwaiter().GetResult()zmieni ślad stosu, aby uwzględnić cały kontekst asynchroniczny, ignorując, że niektóre części kodu są wykonywane w wątku interfejsu użytkownika, a niektóre w wątku puli wątków, a niektóre są po prostu asynchronicznymi we / wy. Więc twój ślad stosu będzie odzwierciedlał synchroniczny krok w kodzie :
TheSyncMethodThatWaitsForTheAsyncMethod
YourCode.SomeAsyncMethod
SomeAsync
YourCode.SomeWork
To sprawia, że ślady stosu wyjątków są co najmniej bardziej przydatne. Możesz zobaczyć, gdzie YourCode.SomeWorkzostało wywołane w kontekście Twojej aplikacji , a nie „fizyczny sposób, w jaki została uruchomiona”.
Przykład tego, jak to działa, znajduje się w źródle referencyjnym (oczywiście poza umową).
TaskAwaiterto szczegół implementacji. Z drugiej strony, mechanizm awaitable / awaiter jest udokumentowany i wykorzystuje pisanie typu kaczego - GetAwaiterjest do tego, awaitco GetEnumeratorjest foreachlub Disposejest do using. Wszystko to jest zdefiniowane w specyfikacji C # niezależnie od używanego konkretnego awaitera - uwaga, że Task.GetAwaiterjest „przeznaczone raczej do użytku przez kompilator niż do użycia w kodzie aplikacji”. Ale chodzi o to, że zamierzonym zastosowaniem jest wykonanie an await, a nie Wait()norGetAwaiter().GetResult() - ale GetResultdaje ładniejsze stosy, jeśli tego potrzebujesz.