Ok, to brzmi dziwnie, ale kod jest bardzo prosty i dobrze wyjaśnia sytuację.
public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
AssertNotDisposed();
var roles = await GetRolesForUser(user);
roles.Roles = RemoveRoles(roles.Roles, role);
await Run(TableOperation.Replace(roles));
}
(Wiem, że mówię trochę w streszczeniu poniżej, ale powyższe jest faktyczną metodą z tego, co będzie rzeczywistym kodem produkcyjnym, który faktycznie robi to, o co tutaj pytam, i jestem naprawdę zainteresowany twoją recenzją dla poprawności względem wzorca asynchronicznego / oczekiwania).
Coraz częściej spotykam się z tym wzorcem, kiedy używam async
/ await
more. Wzór składa się z następującego łańcucha zdarzeń:
- Poczekaj na pierwsze połączenie, które dostarczy mi informacji, nad którymi muszę pracować
- Pracuj nad tymi informacjami synchronicznie
- Poczekaj na ostatnie połączenie, które zapisuje zaktualizowaną pracę
Powyższy blok kodu to zazwyczaj sposób, w jaki zajmuję się obsługą tych metod. I await
pierwsze połączenie, które muszę, ponieważ jest asynchroniczne. Następnie wykonuję pracę, którą muszę wykonać, która nie jest związana z operacjami wejścia / wyjścia ani zasobami, a więc nie jest asynchroniczna. Na koniec oszczędzam swoją pracę, która jest również async
wezwaniem, i poza kultem ładunkowym await
to ja .
Ale czy jest to najbardziej wydajny / prawidłowy sposób obsługi tego wzoru? Wydaje mi się, że mogę pominąć await
ostatnie połączenie, ale co jeśli się nie powiedzie? I czy powinienem zastosować Task
metodę taką jak połączenie ContinueWith
mojej pracy synchronicznej z pierwotnym wywołaniem? Jestem teraz w punkcie, w którym nie jestem pewien, czy radzę sobie z tym poprawnie.
Biorąc pod uwagę kod z przykładu , czy istnieje lepszy sposób na obsługę tego łańcucha wywołań metody async / sync / async?
źródło
Odpowiedzi:
Tak, myślę, że to właściwy sposób, aby to zrobić.
Nie możesz pominąć drugiego
await
. Jeśli tak, metoda wydaje się kończyć zbyt wcześnie (przed faktycznym usunięciem) i nigdy nie dowiesz się, czy usunięcie się nie powiodło.Nie rozumiem, jak by
ContinueWith()
to pomogło. Możesz go użyć, aby tego uniknąćawait
, ale spowodowałoby to, że Twój kod byłby bardziej skomplikowany i mniej czytelny. I o to właśnie chodziawait
: uprościć pisanie kodu asynchronicznego w porównaniu z kontynuacją.źródło
Sposób obsługi tego wzorca polega na zapewnieniu, że wszystkie operacje we / wy są asynchroniczne. Synchroniczne metody we / wy powodują blokowanie bieżącego wątku podczas oczekiwania na odpowiedź z miejsca docelowego we / wy (sieć, system plików itp.).
Inną rzeczą do rozważenia jest to, że
await
powinno się jej używać, gdy potrzebujesz wartości zwracanej lub gdy potrzebujesz kodu, aby zakończyć działanie przed zrobieniem czegoś innego. Jeśli nie potrzebujesz żadnej z tych rzeczy, możesz „odpalić i zapomnieć” swoją metodę asynchronicznąTask.Run
.Tak więc, w celu jak najbardziej efektywnego wykorzystania zasobów obliczeniowych, jeśli
RemoveRoles
jakieś I / O, powinno się stać,await RemoveRolesAsync
a wywoływane metody I / ORemoveRolesAsync
również powinny być asynchroniczne (i być może oczekiwane).Jeśli wydajność nie jest najważniejsza, możesz wykonać synchroniczne operacje we / wy w wątku asynchronicznym. Jest to jednak dług techniczny. (W tym przypadku, to może chcesz się połączyć pierwszą metodę transmisji asynchronicznej z
ConfigureAwait
, w zależności od tego, gdzie jest uruchomiony kod).Oto bardziej dogłębne spojrzenie na najlepsze praktyki - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Oto kilka uwag na temat zachowania ConfigureAwait w różnych środowiskach, takich jak ASP.NET, WebAPI itp. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -kod
źródło