Ist es mit dem asynchronen CTP von Microsoft für .NET möglich, eine Ausnahme abzufangen, die von einer asynchronen Methode in der aufrufenden Methode ausgelöst wird?
public async void Foo()
{
var x = await DoSomethingAsync();
/* Handle the result, but sometimes an exception might be thrown.
For example, DoSomethingAsync gets data from the network
and the data is invalid... a ProtocolException might be thrown. */
}
public void DoFoo()
{
try
{
Foo();
}
catch (ProtocolException ex)
{
/* The exception will never be caught.
Instead when in debug mode, VS2010 will warn and continue.
The deployed the app will simply crash. */
}
}
Grundsätzlich möchte ich, dass die Ausnahme vom asynchronen Code in meinen aufrufenden Code übergeht, wenn dies überhaupt möglich ist.
Antworten:
Es ist etwas seltsam zu lesen, aber ja, die Ausnahme sprudelt bis zum aufrufenden Code - aber nur, wenn Sie
await
oderWait()
der Anruf beiFoo
.Beachten Sie, dass die Verwendung von Wait () dazu führen kann, dass Ihre Anwendung blockiert wird, wenn .Net beschließt, Ihre Methode synchron auszuführen.
Diese Erklärung http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions ist ziemlich gut - sie beschreibt die Schritte, die der Compiler unternimmt, um diese Magie zu erreichen.
quelle
await
den Anruf bei Foo tätigen, wenn Foo ungültig zurückkehrt?async void Foo()
.Type void is not awaitable
?Der Grund, warum die Ausnahme nicht abgefangen wird, liegt darin, dass die Foo () -Methode einen ungültigen Rückgabetyp hat. Wenn also await aufgerufen wird, wird sie einfach zurückgegeben. Da DoFoo () nicht auf den Abschluss von Foo wartet, kann der Ausnahmebehandler nicht verwendet werden.
Es öffnet sich eine einfachere Lösung, wenn Sie die Methodensignaturen ändern - ändern ,
Foo()
so dass es Typ zurückgibtTask
und dannDoFoo()
kannawait Foo()
, wie in diesem Code:quelle
Ihr Code macht nicht das, was Sie vielleicht denken. Asynchrone Methoden kehren sofort zurück, nachdem die Methode auf das asynchrone Ergebnis gewartet hat. Es ist aufschlussreich, die Ablaufverfolgung zu verwenden, um zu untersuchen, wie sich der Code tatsächlich verhält.
Der folgende Code bewirkt Folgendes:
Wenn Sie die Spuren beobachten
Sie werden feststellen, dass die Run-Methode für Thread 2820 abgeschlossen ist, während nur ein untergeordneter Thread abgeschlossen ist (2756). Wenn Sie Ihre Wartemethode versuchen / fangen, können Sie die Ausnahme auf die übliche Weise "fangen", obwohl Ihr Code in einem anderen Thread ausgeführt wird, wenn die Berechnungsaufgabe abgeschlossen ist und Ihre Fortsetzung ausgeführt wird.
Die Berechnungsmethode verfolgt die ausgelöste Ausnahme automatisch, da ich die ApiChange.Api.dll aus dem verwendet habe ApiChange- Tool verwendet habe. Tracing and Reflector hilft sehr zu verstehen, was los ist. Um das Threading zu vermeiden, können Sie Ihre eigenen Versionen von GetAwaiter BeginAwait und EndAwait erstellen und keine Aufgabe, sondern z. B. Lazy and Trace in Ihre eigenen Erweiterungsmethoden einbinden. Dann werden Sie viel besser verstehen, was der Compiler und was die TPL tut.
Jetzt sehen Sie, dass es keine Möglichkeit gibt, Ihre Ausnahme erneut zu versuchen / abzufangen, da kein Stapelrahmen mehr vorhanden ist, von dem aus eine Ausnahme weitergegeben werden kann. Ihr Code macht möglicherweise etwas völlig anderes, nachdem Sie die asynchronen Vorgänge gestartet haben. Es könnte Thread.Sleep aufrufen oder sogar beenden. Solange noch ein Vordergrund-Thread vorhanden ist, führt Ihre Anwendung weiterhin asynchrone Aufgaben aus.
Sie können die Ausnahme innerhalb der asynchronen Methode behandeln, nachdem Ihre asynchrone Operation abgeschlossen wurde, und den UI-Thread zurückrufen. Die empfohlene Methode hierfür ist TaskScheduler.FromSynchronizationContext . Das funktioniert nur, wenn Sie einen UI-Thread haben und er nicht sehr beschäftigt mit anderen Dingen ist.
quelle
Die Ausnahme kann in der asynchronen Funktion abgefangen werden.
quelle
Es ist auch wichtig zu beachten, dass Sie die chronologische Stapelverfolgung der Ausnahme verlieren, wenn Sie einen ungültigen Rückgabetyp für eine asynchrone Methode haben. Ich würde empfehlen, Task wie folgt zurückzugeben. Das Debuggen wird viel einfacher.
quelle
return
Anweisung haben, funktioniert dieser Code jedoch, da derTask
mit "implizit" zurückgegeben wirdasync / await
.Dieser Blog erklärt Ihr Problem genau . Async Best Practices .
Das Wesentliche dabei ist, dass Sie void nicht als Rückgabe für eine asynchrone Methode verwenden sollten, es sei denn, es handelt sich um einen asynchronen Ereignishandler. Dies ist eine schlechte Vorgehensweise, da keine Ausnahmen abgefangen werden können ;-).
Die beste Vorgehensweise wäre, den Rückgabetyp in Task zu ändern. Versuchen Sie außerdem, Async vollständig zu codieren, jeden asynchronen Methodenaufruf auszuführen und von asynchronen Methoden aufgerufen zu werden. Mit Ausnahme einer Main-Methode in einer Konsole, die nicht asynchron sein kann (vor C # 7.1).
Wenn Sie diese bewährte Methode ignorieren, treten bei GUI- und ASP.NET-Anwendungen Deadlocks auf. Der Deadlock tritt auf, weil diese Anwendungen in einem Kontext ausgeführt werden, der nur einen Thread zulässt und ihn nicht an den asynchronen Thread abgibt. Dies bedeutet, dass die GUI synchron auf eine Rückgabe wartet, während die asynchrone Methode auf den Kontext wartet: Deadlock.
Dieses Verhalten tritt in einer Konsolenanwendung nicht auf, da es im Kontext mit einem Thread-Pool ausgeführt wird. Die asynchrone Methode gibt einen anderen Thread zurück, der geplant wird. Aus diesem Grund funktioniert eine Testkonsolen-App, aber die gleichen Aufrufe werden in anderen Anwendungen blockiert ...
quelle