Ich habe eine async
Methode, die keine Daten zurückgibt:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
Ich rufe dies von einer anderen Methode auf, die einige Daten zurückgibt:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Wenn Sie MyAsyncMethod()
ohne Wartezeit anrufen, wird in Visual Studio die Warnung " Da dieser Anruf nicht erwartet wird, wird die aktuelle Methode weiter ausgeführt, bevor der Anruf abgeschlossen ist " angezeigt. Auf der Seite für diese Warnung heißt es:
Sie sollten die Warnung nur unterdrücken, wenn Sie sicher sind, dass Sie nicht auf den Abschluss des asynchronen Aufrufs warten möchten und die aufgerufene Methode keine Ausnahmen auslöst .
Ich bin sicher, ich möchte nicht warten, bis der Anruf abgeschlossen ist. Ich brauche oder habe keine Zeit dafür. Der Anruf kann jedoch Ausnahmen auslösen.
Ich bin ein paar Mal auf dieses Problem gestoßen und bin mir sicher, dass es ein häufiges Problem ist, das eine gemeinsame Lösung haben muss.
Wie rufe ich eine asynchrone Methode sicher auf, ohne auf das Ergebnis zu warten?
Aktualisieren:
Für Leute, die vorschlagen, dass ich nur auf das Ergebnis warte, ist dies Code, der auf eine Webanforderung in unserem Webdienst (ASP.NET-Web-API) reagiert. Das Warten in einem UI-Kontext hält den UI-Thread frei, aber das Warten in einem Webanforderungsaufruf wartet, bis die Aufgabe beendet ist, bevor auf die Anfrage geantwortet wird, wodurch die Antwortzeiten ohne Grund verlängert werden.
quelle
MyAsyncMethod().Wait()
Antworten:
Wenn Sie die Ausnahme "asynchron" erhalten möchten, können Sie Folgendes tun:
Auf diese Weise können Sie eine Ausnahme in einem anderen Thread als dem "Haupt" -Thread behandeln. Dies bedeutet, dass Sie nicht auf den Aufruf des aufrufenden
MyAsyncMethod()
Threads "warten" müssenMyAsyncMethod
. Sie können jedoch weiterhin etwas mit einer Ausnahme tun - jedoch nur, wenn eine Ausnahme auftritt.Aktualisieren:
technisch könnte man etwas ähnliches machen mit
await
:... was nützlich wäre, wenn Sie
try
/catch
(oderusing
) speziell verwenden müssten, aber ich finde dasContinueWith
etwas expliziter, weil Sie wissen müssen, wasConfigureAwait(false)
bedeutet.quelle
Task
: public static class AsyncUtility {public static void PerformAsyncTaskWithoutAwait (diese Task-Task, Aktion <Task> exceptionHandler) {var dummy = task.ContinueWith (t => exceptionHandler (t), TaskContinuationOptions.OnlyOnFaulted); }} Verwendung: MyAsyncMethod (). PerformAsyncTaskWithoutAwait (t => log.ErrorFormat ("Beim Aufrufen von MyAsyncMethod ist ein Fehler aufgetreten: \ n {0}", t.Exception));ConfiguratAwait(false)
wird erst ausgeführt, wenn die Aufgabe abgeschlossen ist, aber der aktuelle Thread "wartet" nicht (dh blockiert) darauf. Die nächste Zeile wird asynchron zum Aufruf des Wartens aufgerufen. Ohne das würdeConfigureAwait(false)
die nächste Zeile im ursprünglichen Webanforderungskontext ausgeführt. Mit demConfigurateAwait(false)
ist es in diesem Zusammenhang , wie der Asynchron - Methode (Task) ausgeführt wird , den ursprünglichen Kontext / Thread befreit auf , um fortzufahren ...Sie sollten zuerst überlegen,
GetStringData
eineasync
Methode zu erstellen undawait
die Aufgabe von ihr zurückgeben zu lassenMyAsyncMethod
.Wenn Sie absolut sicher sind, dass Sie keine Ausnahmen behandeln müssen
MyAsyncMethod
oder wissen, wann sie abgeschlossen sind, können Sie Folgendes tun:Übrigens ist dies kein "häufiges Problem". Es ist sehr selten, dass Sie Code ausführen möchten und sich nicht darum kümmern möchten, ob er abgeschlossen wird, und nicht darum, ob er erfolgreich abgeschlossen wird.
Aktualisieren:
Da Sie auf ASP.NET sind und frühzeitig zurückkehren möchten, ist mein Blog-Beitrag zu diesem Thema möglicherweise hilfreich . ASP.NET wurde jedoch nicht dafür entwickelt, und es gibt keine Garantie dafür, dass Ihr Code ausgeführt wird, nachdem die Antwort zurückgegeben wurde. ASP.NET wird sein Bestes tun, um es laufen zu lassen, kann es jedoch nicht garantieren.
Dies ist also eine gute Lösung für etwas Einfaches wie das Werfen eines Ereignisses in ein Protokoll, in dem es nicht wirklich wichtig ist, ob Sie hier und da ein paar verlieren. Es ist keine gute Lösung für geschäftskritische Vorgänge. In diesen Situationen Sie müssen eine komplexere Architektur verabschieden, mit persistent die Operationen (zB Azure Queues, MSMQ) und einem separaten Hintergrundprozess (zB Azure Worker Role, Win32 - Dienst) , um sie zu verarbeiten , zu speichern.
quelle
var _ = MyAsyncMethod();
mit_ = MyAsyncMethod();
. Dadurch wird die Warnung CS4014 weiterhin vermieden, es wird jedoch etwas deutlicher, dass Sie die Variable nicht verwenden.Die Antwort von Peter Ritchie war genau das, was ich wollte, und Stephen Clearys Artikel über die frühzeitige Rückkehr in ASP.NET war sehr hilfreich.
Als allgemeineres Problem (nicht spezifisch für einen ASP.NET-Kontext) demonstriert die folgende Konsolenanwendung die Verwendung und das Verhalten von Peters Antwort mit
Task.ContinueWith(...)
GetStringData()
Rückgabe frühzeitig ohne WartezeitMyAsyncMethod()
und eingeworfene AusnahmenMyAsyncMethod()
werden inOnMyAsyncMethodFailed(Task task)
und nicht intry
/catch
um behandeltGetStringData()
quelle
Console.ReadLine();
und fügen Sie ein wenig Schlaf / Verzögerung hinzu,MyAsyncMethod
und Sie werden nie die Ausnahme sehen.Am Ende habe ich diese Lösung:
quelle
Dies nennt man Feuer und Vergessen, und dafür gibt es eine Erweiterung .
Installieren Sie das Nuget-Paket .
Verwenden:
quelle
Ich denke, es stellt sich die Frage, warum Sie das tun müssen. Der Grund für
async
in C # 5.0 ist, dass Sie auf ein Ergebnis warten können. Diese Methode ist eigentlich nicht asynchron, sondern wird jeweils nur aufgerufen, um den aktuellen Thread nicht zu stark zu stören.Vielleicht ist es besser, einen Thread zu starten und ihn alleine zu beenden.
quelle
async
ist ein bisschen mehr als nur ein Ergebnis "abzuwarten". "Warten" bedeutet, dass die Zeilen nach "Warten" asynchron auf demselben Thread ausgeführt werden, der "Warten" aufgerufen hat. Dies kann natürlich ohne "Warten" geschehen, aber am Ende haben Sie eine Reihe von Delegierten und verlieren das sequenzielle Erscheinungsbild des Codes (sowie die Fähigkeit zu verwendenusing
undtry/catch
...await
Schlüsselwort nicht verwendet und das Schlüsselwort nicht verwendetasync
, aber es macht überhaupt keinen Sinn, dasasync
Schlüsselwort zu verwenden, ohne es auchawait
in der Definition dieser Methode zu verwenden.async
ist ein bisschen mehr als nur" auf "ein Ergebnis zu warten". Dasasync
Schlüsselwort (Sie haben impliziert, dass es das Schlüsselwort ist, indem Sie es in Backticks einschließen) bedeutet nichts weiter als auf das Ergebnis zu warten. Es ist Asynchronität, wie die allgemeinen CS-Konzepte, die mehr bedeutet, als nur auf ein Ergebnis zu warten.async
erstellt eine Zustandsmaschine, die alle Wartezeiten innerhalb der asynchronen Methode verwaltet. Wennawait
die Methode kein s enthält, wird diese Zustandsmaschine weiterhin erstellt, die Methode ist jedoch nicht asynchron. Und wenn dieasync
Methode zurückkehrtvoid
, gibt es nichts zu erwarten. Es ist also mehr als nur ein Ergebnis abzuwarten.async
Schlüsselwort sind nicht erforderlich . Alles, was Sie tun müssen (und alles, was am Ende mit einer Zustandsmaschine in diesem speziellen Fall wirklich passiert), ist, dass die Methode synchron ausgeführt und dann in eine abgeschlossene Aufgabe eingeschlossen wird. Ich nehme an, technisch gesehen entfernen Sie nicht nur die Zustandsmaschine. Sie entfernen die Zustandsmaschine und rufen dann aufTask.FromResult
. Ich ging davon aus, dass Sie (und auch die Compiler-Autoren) das Addendum selbst hinzufügen könnten.Bei Technologien mit Nachrichtenschleifen (nicht sicher, ob ASP einer von ihnen ist) können Sie die Schleife blockieren und Nachrichten verarbeiten, bis die Aufgabe beendet ist, und mit ContinueWith den Code entsperren:
Dieser Ansatz ähnelt dem Blockieren von ShowDialog und dem Anhalten der Benutzeroberfläche.
quelle
Ich bin zu spät zur Party hier, aber es gibt eine großartige Bibliothek, die ich benutzt habe und auf die ich in den anderen Antworten nicht verwiesen habe
https://github.com/brminnick/AsyncAwaitBestPractices
Wenn Sie "Fire And Forget" benötigen, rufen Sie die Erweiterungsmethode für die Aufgabe auf.
Durch das Übergeben der Aktion onException an den Aufruf wird sichergestellt, dass Sie das Beste aus beiden Welten erhalten - Sie müssen nicht auf die Ausführung warten und Ihre Benutzer verlangsamen, während die Möglichkeit erhalten bleibt, die Ausnahme auf elegante Weise zu behandeln.
In Ihrem Beispiel würden Sie es folgendermaßen verwenden:
Es gibt auch erwartete AsyncCommands, die ICommand sofort implementieren, was für meine MVVM Xamarin-Lösung großartig ist
quelle
Die Lösung besteht darin, den HttpClient in eine andere Ausführungsaufgabe ohne Sincronisierungskontext zu starten:
quelle
Wenn Sie das wirklich wollen. Nur um "Async-Methode in C # ohne Wartezeit aufrufen" zu adressieren, können Sie die Async-Methode in a ausführen
Task.Run
. Dieser Ansatz wartet bis zumMyAsyncMethod
Ende.await
Entpackt dieResult
Aufgabe asynchron , während nur die Verwendung von Ergebnis blockiert wird, bis die Aufgabe abgeschlossen ist.quelle
Normalerweise gibt die asynchrone Methode die Task-Klasse zurück. Wenn Sie eine
Wait()
Methode oder eineResult
Eigenschaft verwenden und Code eine Ausnahme auslöst - der Ausnahmetyp wird eingeschlossenAggregateException
-, müssen Sie eine Abfrage durchführenException.InnerException
, um die richtige Ausnahme zu finden.Es ist aber auch möglich zu verwenden
.GetAwaiter().GetResult()
stattdessen eine asynchrone Aufgabe zu warten, aber keine Ausnahme zu verpacken.Hier ist ein kurzes Beispiel:
Möglicherweise möchten Sie auch in der Lage sein, einige Parameter von der asynchronen Funktion zurückzugeben.
Action<return type>
Dies kann erreicht werden, indem Sie beispielsweise zusätzliche Funktionen für die asynchrone Funktion bereitstellen :Beachten Sie, dass asynchrone Methoden normalerweise Suffixnamen haben
ASync
, um Kollisionen zwischen Synchronisierungsfunktionen mit demselben Namen zu vermeiden. (ZBFileStream.ReadAsync
) - Ich habe Funktionsnamen aktualisiert, um dieser Empfehlung zu folgen.quelle