2011-06-16 25 views
8

Este ejemplo "falla":excepciones Try-Catch asíncronas

static async void Main(string[] args) 
{ 
    try 
    { 
     await TaskEx.Run(() => { throw new Exception("failure"); }); 
    } 
    catch (Exception) 
    { 
     throw new Exception("success"); 
    } 
} 

Es decir, la excepción con el texto "fracaso" se propaga hacia arriba.

Luego probé esta solución:

static async void Main(string[] args) 
{ 
    try 
    { 
     await SafeRun(() => { throw new Exception("failure"); }); 
    } 
    catch (Exception) 
    { 
     throw new Exception("success"); 
    } 
} 

static async Task SafeRun(Action action) 
{ 
    var ex = default(Exception); 
    await TaskEx.Run(() => 
    { 
     try 
     { 
      action(); 
     } 
     catch (Exception _) 
     { 
      ex = _; 
     } 
    }); 
    if (ex != default(Exception)) 
     throw ex; 
} 

Eso no ayudó tampoco.

Supongo que mi instalación de refresco Async CTP podría ser una manguera.

En caso de que este código funcione como espero ("éxito" aumenta, no "falla"), o no se "supone" que funciona de esa manera. Y si no, ¿cómo lo solucionarías?

Respuesta

5

El comportamiento que está viendo es probable que sea un error de borde o incluso puede ser correcto, si no es intuitivo. Normalmente, cuando invoca un método asíncrono sincrónicamente, envuelve una tarea para ejecutar y, dado que no hay nadie esperando que la tarea finalice, la excepción nunca llega al hilo principal. Si tuviera que llamar a Main directamente, tendría éxito, pero luego su tiempo de ejecución vería una excepción de "éxito" en otro hilo.

Dado que main es el punto de entrada de su aplicación, se invoca de forma síncrona y probablemente porque el punto de entrada no desencadena el comportamiento de envoltura de tareas, por lo que espera no se ejecuta correctamente y TaskEx.Run ejecuta su propio hilo, que se muestra en el tiempo de ejecución como una excepción lanzada en otro hilo.

Si se va a ejecutar principal como un método async, es decir, el retorno de una Task (ya que un async que devuelve void sólo puede realmente ser llamado a través de await) y el bloqueo en él desde su contexto principal sincrónica, se llega a la conducta apropiada como la siguiente prueba ilustra:

static async Task Main() { 
    try { 
     await TaskEx.Run(() => { throw new Exception("failure"); }); 
    } catch(Exception) { 
     throw new Exception("success"); 
    } 
} 

static async Task Main2() { 
    await Main(); 
} 

[Test] 
public void CallViaAwait() { 
    var t = Main2(); 
    try { 
     t.Wait(); 
     Assert.Fail("didn't throw"); 
    } catch(AggregateException e) { 
     Assert.AreEqual("success",e.InnerException.Message); 
    } 
    } 


[Test] 
public void CallDirectly() { 
    var t = Main(); 
    try { 
     t.Wait(); 
     Assert.Fail("didn't throw"); 
    } catch(AggregateException e) { 
     Assert.AreEqual("success", e.InnerException.Message); 
    } 
} 

Ie la tarea falla con un AggregateException que contiene la excepción de éxito , ya que es la excepción interna.

+0

Es a partir de una aplicación de consola para probar asincrónico, por lo que el método de nivel superior es el método principal en mi caso. –

+0

@Bent he actualizado la respuesta para reflejar cómo está llamando, lo que puede o no ser un error, pero es causado por un punto de entrada 'async' que no se define como comportamiento –

Cuestiones relacionadas