Mam async
metodę, która nie zwraca danych:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
Wywołuję to z innej metody, która zwraca niektóre dane:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Wywołanie MyAsyncMethod()
bez oczekiwania powoduje pojawienie się ostrzeżenia „ Ponieważ to połączenie nie jest oczekiwane, bieżąca metoda działa jeszcze przed zakończeniem połączenia ” w Visual Studio. Na stronie tego ostrzeżenia znajduje się:
Powinieneś rozważyć wyłączenie ostrzeżenia tylko wtedy, gdy masz pewność, że nie chcesz czekać na zakończenie asynchronicznego wywołania i że wywoływana metoda nie spowoduje żadnych wyjątków .
Jestem pewien, że nie chcę czekać na zakończenie połączenia; Nie muszę ani nie mam czasu. Ale połączenie może powodować wyjątki.
Kilka razy natknąłem się na ten problem i jestem pewien, że jest to powszechny problem, który musi mieć wspólne rozwiązanie.
Jak bezpiecznie wywołać metodę asynchroniczną bez oczekiwania na wynik?
Aktualizacja:
Dla osób sugerujących, że właśnie czekam na wynik, jest to kod, który odpowiada na żądanie sieciowe w naszym serwisie internetowym (ASP.NET Web API). Oczekiwanie w kontekście interfejsu użytkownika utrzymuje wątek interfejsu użytkownika wolny, ale oczekiwanie w wywołaniu żądania WWW będzie czekać na zakończenie zadania przed odpowiedzią na żądanie, zwiększając w ten sposób czas odpowiedzi bez powodu.
źródło
MyAsyncMethod().Wait()
Odpowiedzi:
Jeśli chcesz uzyskać wyjątek „asynchronicznie”, możesz:
To pozwoli ci poradzić sobie z wyjątkiem w wątku innym niż wątek „główny”. Oznacza to, że nie musisz „czekać” na połączenie
MyAsyncMethod()
z wątku, który wywołujeMyAsyncMethod
; ale nadal umożliwia zrobienie czegoś z wyjątkiem - ale tylko w przypadku wystąpienia wyjątku.Aktualizacja:
technicznie możesz zrobić coś podobnego z
await
:... co byłoby przydatne, gdybyś potrzebował konkretnie użyć
try
/catch
(lubusing
), ale uważam, żeContinueWith
jest to nieco bardziej wyraźne, ponieważ musisz wiedzieć, coConfigureAwait(false)
oznacza.źródło
Task
: public static static AsyncUtility {public static void PerformAsyncTaskWithoutAwait (to zadanie Task, Action <Task> wyjmowanieHandler) {var dummy = task.ContinueWith (t => wyjmowanieHandler (t), TaskContinuationOptions.OnlyOnFaulted); }} Zastosowanie: MyAsyncMethod (). PerformAsyncTaskWithoutAwait (t => log.ErrorFormat („Wystąpił błąd podczas wywoływania MyAsyncMethod: \ n {0}", t.Exception));ConfiguratAwait(false)
nie jest wykonywany, dopóki zadanie się nie zakończy, ale bieżący wątek nie „czeka” (tj. Blok) na to, następny wiersz jest wywoływany asynchronicznie do wywołania oczekującego. BezConfigureAwait(false)
tego następny wiersz zostałby wykonany w oryginalnym kontekście żądania internetowego. DziękiConfigurateAwait(false)
temu jest wykonywany w tym samym kontekście co metoda asynchroniczna (zadanie), uwalniając oryginalny kontekst / wątek, aby kontynuować ...Najpierw zastanów się nad
GetStringData
stworzeniemasync
metody i zwróć jąawait
z zadaniaMyAsyncMethod
.Jeśli masz absolutną pewność, że nie musisz obsługiwać wyjątków od
MyAsyncMethod
ani wiedzieć, kiedy się ono spełni, możesz to zrobić:BTW, to nie jest „powszechny problem”. Bardzo rzadko chcemy wykonać jakiś kod i nie przejmować się, czy się on skończy i nie obchodzi, czy się to powiedzie.
Aktualizacja:
Ponieważ jesteś na ASP.NET i chcesz wrócić wcześniej, może się okazać, że mój post na blogu na ten temat jest przydatny . Jednak ASP.NET nie został do tego przeznaczony i nie ma gwarancji, że kod zostanie uruchomiony po zwróceniu odpowiedzi. ASP.NET dołoży wszelkich starań, aby pozwolić mu działać, ale nie może tego zagwarantować.
Jest to więc dobre rozwiązanie dla czegoś tak prostego, jak wrzucenie zdarzenia do dziennika, w którym tak naprawdę nie ma znaczenia, jeśli stracisz kilka tu i tam. To nie jest dobre rozwiązanie dla wszelkiego rodzaju operacji o znaczeniu krytycznym. W takich sytuacjach musisz przyjąć bardziej złożoną architekturę, z trwałym sposobem zapisywania operacji (np. Kolejki Azure, MSMQ) i osobnym procesem w tle (np. Rola pracownika Azure, usługa Win32), aby je przetworzyć.
źródło
var _ = MyAsyncMethod();
z_ = MyAsyncMethod();
. To wciąż pozwala uniknąć ostrzeżenia CS4014, ale czyni to bardziej wyraźnym, że nie używasz zmiennej.Odpowiedzi udzielił Peter Ritchie i artykuł Stephena Cleary'ego o powrocie wcześniej w ASP.NET był bardzo pomocny.
Jednak jako bardziej ogólny problem (nie specyficzny dla kontekstu ASP.NET) następująca aplikacja konsoli demonstruje użycie i zachowanie odpowiedzi Petera przy użyciu
Task.ContinueWith(...)
GetStringData()
wraca wcześnie bez oczekiwania,MyAsyncMethod()
a zgłoszone wyjątkiMyAsyncMethod()
są rozpatrywane w,OnMyAsyncMethodFailed(Task task)
a nie wtry
/catch
dookołaGetStringData()
źródło
Console.ReadLine();
i dodaj trochę snu / opóźnienia,MyAsyncMethod
a nigdy nie zobaczysz wyjątku.Kończę z tym rozwiązaniem:
źródło
To się nazywa ogień i zapomnij, i jest na to rozszerzenie .
Zainstaluj pakiet nuget .
Posługiwać się:
źródło
Myślę, że powstaje pytanie, dlaczego miałbyś to zrobić? Powodem
async
w C # 5.0 jest, więc możesz poczekać na wynik. Ta metoda nie jest tak naprawdę asynchroniczna, ale po prostu wywoływana na raz, aby nie zakłócać zbytnio bieżącego wątku.Być może lepiej jest rozpocząć wątek i pozostawić go skończeniu.
źródło
async
to coś więcej niż „oczekiwanie” na wynik. „Oczekiwanie” oznacza, że linie następujące po „Oczekiwanie” są wykonywane asynchronicznie w tym samym wątku, który wywołał „Oczekiwanie”. Można to oczywiście zrobić bez „czekania”, ale ostatecznie masz grupę delegatów i tracisz sekwencyjny wygląd kodu (a także możliwość używaniausing
itry/catch
...await
słowa kluczowego i nie używaasync
słowa kluczowego, ale nie ma sensu używanieasync
słowa kluczowego bez uwzględnienia goawait
w definicji tej metody.async
to coś więcej niż„ oczekiwanie ”na wynik”. Słowoasync
kluczowe (zasugerowałeś, że jest słowem kluczowym, umieszczając je w backticks) oznacza tylko oczekiwanie na wynik. Jest to asynchronia, jako ogólne koncepcje CS, oznacza więcej niż tylko oczekiwanie na wynik.async
tworzy maszynę stanu, która zarządza wszelkimi oczekiwaniami w ramach metody asynchronicznej. Jeśliawait
w metodzie nie ma żadnych znaków, nadal tworzy on maszynę stanu - ale metoda nie jest asynchroniczna. A jeśliasync
metoda powrócivoid
, nie ma na co czekać. To coś więcej niż tylko oczekiwanie na wynik.async
słowa kluczowego. Wszystko, co musicie zrobić (i wszystko, co naprawdę dzieje się ostatecznie z maszyną stanu w tym szczególnym przypadku), to uruchomienie metody synchronicznej, a następnie zawinięcie jej w zakończone zadanie. Przypuszczam, że technicznie nie usuwasz tylko maszyny stanu; usuwasz automat stanowy, a następnie dzwoniszTask.FromResult
. Zakładałem, że ty (a także autorzy kompilatorów) możesz samodzielnie dodać ten dodatek.W technologiach z pętlami komunikatów (nie jestem pewien, czy ASP jest jedną z nich), możesz zablokować pętlę i przetwarzać wiadomości aż do zakończenia zadania, a następnie użyj ContinueWith, aby odblokować kod:
To podejście jest podobne do blokowania w ShowDialog i nadal utrzymywania interfejsu użytkownika w gotowości.
źródło
Spóźniłem się na przyjęcie tutaj, ale jest niesamowita biblioteka, z której korzystałem, o której nie widziałem w innych odpowiedziach
https://github.com/brminnick/AsyncAwaitBestPractices
Jeśli potrzebujesz „Fire And Forget”, wywołujesz metodę rozszerzenia zadania.
Przekazanie akcji wyjątku do wywołania zapewnia, że uzyskasz to, co najlepsze z obu światów - nie musisz czekać na wykonanie i spowolnić użytkowników, zachowując jednocześnie możliwość obsługi wyjątku w sposób wdzięczny.
W twoim przykładzie użyłbyś tego w ten sposób:
Daje również oczekiwane AsyncCommands wdrażające ICommand po wyjęciu z pudełka, co jest świetne dla mojego rozwiązania Xamarin MVVM
źródło
Rozwiązaniem jest uruchomienie HttpClient do innego zadania wykonawczego bez kontekstu sincronization:
źródło
Jeśli naprawdę chcesz to zrobić. Aby rozwiązać problem „Wywołaj metodę asynchroniczną w języku C # bez oczekiwania”, możesz wykonać metodę asynchroniczną w pliku a
Task.Run
. Takie podejście będzie czekać doMyAsyncMethod
końca.await
asynchronicznie rozpakowujeResult
zadanie, podczas gdy samo użycie wyniku blokowałoby, aż zadanie się zakończy.źródło
Zazwyczaj metoda asynchroniczna zwraca klasę zadania. Jeśli użyjesz
Wait()
metody lubResult
właściwości, a kod zgłasza wyjątek - typ wyjątku zostaje zawiniętyAggregateException
- musisz zapytać,Exception.InnerException
aby znaleźć poprawny wyjątek.Ale można również użyć
.GetAwaiter().GetResult()
zamiast tego - będzie również czekać na zadanie asynchroniczne, ale nie zawinie wyjątku.Oto krótki przykład:
Możesz także chcieć móc zwrócić jakiś parametr z funkcji asynchronicznej - można to osiągnąć poprzez dodanie dodatkowej
Action<return type>
funkcji asynchronicznej, na przykład:Należy pamiętać, że metody asynchroniczne zwykle mają
ASync
nazywanie sufiksami, aby uniknąć kolizji między funkcjami synchronizacji o tej samej nazwie. (Np.FileStream.ReadAsync
) - Zaktualizowałem nazwy funkcji, aby zastosować się do tego zalecenia.źródło