2012-09-07 26 views
18

Cuando un método async que se espera arroja una excepción, la excepción se almacena en algún lugar y su lanzamiento se retrasa. En una aplicación WinForms o WPF, usa SynchronizationContext.Current para publicar el lanzamiento de la excepción. Sin embargo, en, por ejemplo, una aplicación de consola, lanza la excepción en un grupo de subprocesos y baja la aplicación.Capturar excepciones no controladas de async

¿Cómo puedo evitar que las excepciones lanzadas desde un método async dejen la aplicación?

EDIT:

appearantly el tema que estoy describiendo es porque tengo voidasync métodos. Ver comentarios.

+2

Si se espera un método asíncrono, se emite la excepción en el código que lo espera. Las excepciones no controladas se comportan de esta manera solo si el método es 'void' regresar. Esta es una razón para evitar el uso de métodos 'async void' tanto como sea posible. – svick

+1

@svick 'async void' métodos dan como resultado un comportamiento determinista; Llamar a una función de devolución de 'Tarea' que da como resultado una excepción y no espera la tarea levantará el evento' UnobservedTaskException' en algún punto semiandomiano en el futuro cuando se ejecuta el recolector de elementos no utilizados, y si eso no hace nada, permite que el programa continúe en silencio si todo está bien. El problema no está en los métodos 'async void', simplemente exponen el problema real. Si * no * llama a una función 'que devuelve' Tarea' de un método 'async', hay muchas posibilidades de que esté haciendo algo mal. – hvd

+0

Por cierto, solo "buena oportunidad" porque hay algunos casos excepcionales (sin juego de palabras) donde está bien descartar excepciones, pero no mucho. – hvd

Respuesta

10

Cuando se inicia el método async, captura el contexto de sincronización actual. Una forma de resolver este problema es crear su propio contexto de sincronización que capture la excepción.

El punto aquí es que los puestos de contexto de sincronización de la repetición de llamada para el grupo de subprocesos, pero con un try/catch alrededor de él:

public class AsyncSynchronizationContext : SynchronizationContext 
{ 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 

    public override void Post(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 
} 

En el catch por encima de usted puede poner su lógica de manejo de excepciones.

A continuación, en cada hilo (SynchronizationContext.Current es [ThreadStatic]) en la que desea ejecutar async métodos con este mecanismo, debe establecer el contexto de sincronización actual:

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

La completa Main ejemplo:

class Program 
{ 
    static void Main(string[] args) 
    { 
     SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

     ExecuteAsyncMethod(); 

     Console.ReadKey(); 
    } 

    private static async void ExecuteAsyncMethod() 
    { 
     await AsyncMethod(); 
    } 

    private static async Task AsyncMethod() 
    { 
     throw new Exception("Exception from async"); 
    } 
} 
+3

Lo siento, pero no veo dónde exactamente su SynchronizationContext publica nada en el grupo de subprocesos; por lo que puedo ver, todo lo que hace es ejecutar la función inmediatamente en el hilo que llama y lo devuelve. –

18

¿Cómo puedo evitar que las excepciones lanzadas desde un método asincrónico derriben la aplicación?

siga estos procedimientos recomendados:

  1. Todo async métodos deben volver Task o Task<T> menos que tienen que volver void (por ejemplo, controladores de eventos).
  2. En algún momento, debe await todos Task s devueltos desde async métodos. La única razón por la que no desea hacer esto es si ya no le importa el resultado de la operación (por ejemplo, después de cancelarla).
  3. Si necesita capturar una excepción de un controlador de eventos async void, tóquelo en el controlador de eventos, exactamente como lo haría si se tratara de un código síncrono.

Puede que encuentre async/await intro post útil; También cubro otras buenas prácticas allí.

+1

Entiendo que el problema que estoy recibiendo es que tengo los métodos 'void'' async'. Sin embargo, ¿esto significa que el problema que estoy describiendo ocurre para los manejadores de eventos? –

+0

Sí; para reiterar el tercer punto, si tiene un controlador de eventos 'async void', probablemente quiera detectar excepciones * dentro de ese controlador de eventos. Un controlador de eventos síncrono tendrá exactamente el mismo problema: bloqueará la aplicación si se produce una excepción mientras se ejecuta en un hilo del grupo de subprocesos. –

+0

Excepto que las excepciones se enrutan al 'SynchronizationContext' que se capturó cuando se invocó el método' async'. En una aplicación WinForms/WPF, la mayoría de las veces este será un contexto de sincronización correcto ya que el evento probablemente se haya iniciado desde el hilo de la interfaz de usuario. Parece que el comportamiento no es tan diferente de lo que sucede cuando el evento no es 'async'. –

Cuestiones relacionadas