Usando el CTP asíncrono de Microsoft para .NET, ¿es posible detectar una excepción lanzada por un método asíncrono en el método de llamada?
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. */
}
}
Así que, básicamente, quiero que la excepción del código asíncrono aparezca en mi código de llamada si eso es posible.
Respuestas:
Es algo extraño de leer, pero sí, la excepción aparecerá en el código de llamada, pero solo si usted
await
oWait()
la llamada lo hacenFoo
.Tenga en cuenta que el uso de Wait () puede hacer que su aplicación se bloquee, si .Net decide ejecutar su método sincrónicamente.
Esta explicación http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions es bastante buena: analiza los pasos que toma el compilador para lograr esta magia.
fuente
await
llamar a Foo cuando Foo regresa vacío?async void Foo()
.Type void is not awaitable
?La razón por la que no se detecta la excepción es porque el método Foo () tiene un tipo de retorno nulo y, por lo tanto, cuando se llama a wait, simplemente regresa. Como DoFoo () no espera la finalización de Foo, no se puede usar el controlador de excepciones.
Esto abre una solución más simple si puede cambiar las firmas del método: modifique
Foo()
para que devuelva el tipoTask
y luegoDoFoo()
puedaawait Foo()
, como en este código:fuente
Su código no hace lo que podría pensar que hace. Los métodos asíncronos regresan inmediatamente después de que el método comienza a esperar el resultado asíncrono. Es útil utilizar el rastreo para investigar cómo se comporta realmente el código.
El siguiente código hace lo siguiente:
Cuando observas las huellas
Notará que el método Run se completa en el hilo 2820 mientras que solo un hilo hijo ha terminado (2756). Si pone un método try / catch alrededor de su método de espera, puede "capturar" la excepción de la manera habitual, aunque su código se ejecuta en otro hilo cuando la tarea de cálculo ha finalizado y se ejecuta su continuación.
El método de cálculo rastrea la excepción lanzada automáticamente porque utilicé ApiChange.Api.dll de la herramienta ApiChange . Tracing and Reflector ayuda mucho a comprender lo que está sucediendo. Para deshacerse de los subprocesos, puede crear sus propias versiones de GetAwaiter BeginAwait y EndAwait y ajustar no una tarea sino, por ejemplo, un Lazy y rastrear dentro de sus propios métodos de extensión. Entonces comprenderá mucho mejor lo que hace el compilador y lo que hace el TPL.
Ahora verá que no hay forma de volver a intentar / atrapar su excepción ya que no queda ningún marco de pila para que se propague cualquier excepción. Su código podría estar haciendo algo totalmente diferente después de haber iniciado las operaciones asíncronas. Puede llamar a Thread.Sleep o incluso terminar. Mientras quede un subproceso en primer plano, su aplicación seguirá felizmente ejecutando tareas asincrónicas.
Puede manejar la excepción dentro del método asincrónico después de que su operación asincrónica haya finalizado y volver a llamar al hilo de la interfaz de usuario. La forma recomendada de hacerlo es con TaskScheduler.FromSynchronizationContext . Eso solo funciona si tiene un subproceso de interfaz de usuario y no está muy ocupado con otras cosas.
fuente
La excepción se puede detectar en la función asíncrona.
fuente
También es importante tener en cuenta que perderá el rastro cronológico de la pila de la excepción si tiene un tipo de retorno nulo en un método asíncrono. Recomendaría devolver la tarea de la siguiente manera. Voy a hacer que la depuración sea mucho más fácil.
fuente
return
declaración, este código funciona, ya queTask
se devuelve "implícitamente" mediante el usoasync / await
.Este blog explica su problema de forma ordenada. Mejores prácticas asíncronas .
La esencia de esto es que no deberías usar void como retorno para un método asíncrono, a menos que sea un controlador de eventos asíncrono, esta es una mala práctica porque no permite detectar excepciones ;-).
La mejor práctica sería cambiar el tipo de retorno a Tarea. Además, intente codificar asíncrono hasta el final, realice todas las llamadas a métodos asincrónicos y reciba llamadas de métodos asincrónicos. Excepto por un método Main en una consola, que no puede ser asíncrono (antes de C # 7.1).
Te encontrarás con puntos muertos con aplicaciones GUI y ASP.NET si ignoras esta práctica recomendada. El punto muerto se produce porque estas aplicaciones se ejecutan en un contexto que permite solo un subproceso y no lo cederá al subproceso asíncrono. Esto significa que la GUI espera sincrónicamente un retorno, mientras que el método asíncrono espera el contexto: punto muerto.
Este comportamiento no ocurrirá en una aplicación de consola, porque se ejecuta en contexto con un grupo de subprocesos. El método asíncrono volverá en otro hilo que se programará. Es por eso que una aplicación de consola de prueba funcionará, pero las mismas llamadas se estancarán en otras aplicaciones ...
fuente