2010-10-19 10 views
6

Basado en mi investigación, he aprendido lo siguiente:TaskScheduler.UnobservedTaskException nunca se llama

  1. TaskScheduler.UnobservedTaskException debe esperar a que la tarea sea recolectado antes excepción no observada de esa tarea que estará circulando hasta el evento UnobservedTaskException.
  2. , si nunca usas Task.Wait(), nunca se llamará de todos modos, porque estás bloqueando un resultado inminente de la Tarea, por lo tanto, la excepción se lanzará al Task.Wait() en lugar del evento UnobservedException.
  3. Llamar a GC.Collect() manualmente es generalmente una mala idea a menos que sepa exactamente lo que está haciendo, por lo tanto, es bueno en este caso para confirmar las cosas, pero no como una solución adecuada al problema.

El problema

Si mi aplicación se cierra antes de que el recolector de basura entra en acción, estoy totalmente de 100% no puede conseguir mi evento UnobservedTaskException al fuego.

Nota el siguiente código:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
     Task.Factory.StartNew(() => 
     { 
      Console.WriteLine("Task started."); 
      throw new Exception("Test Exception"); 
     }); 
     Thread.Sleep(1000); 
     //GC.Collect(); 
     //GC.WaitForPendingFinalizers(); 
    } 

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
    { 
     File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString()); 
     Console.WriteLine("EXCEPTION UNOBSERVED"); 
    } 
} 

Sin archivo de excepciones está escrito y nada se escribe en la consola. Pueden transcurrir de 10 a 15 minutos y más después de que la aplicación finalice y aún así no veo evidencia de que los restos de mi aplicación hayan sido recolectados. Podrías preguntar, ¿por qué no solo cobrar a la salida? Bueno, mi escenario en el mundo real es que mi captura de excepciones se ejecuta dentro de un servicio WCF alojado dentro de un servicio de Windows. No puedo interceptar cuando el servicio de Windows se está cerrando (y por lo tanto llamar manualmente al GC.Collect()) porque no hay ningún evento para el que yo pueda ver.

¿Dónde me equivoco? ¿Cómo me aseguro de que si algo profundamente dentro del servicio WCF finalmente va a romper mi servicio de Windows, tengo la oportunidad de registrar la excepción antes de que el servicio se caiga?

Respuesta

2

Nathan,

Usted estés puntos son ciertas. Pruebe lo siguiente:

namespace ConsoleApplication1 
{ 
    using System; 
    using System.Threading; 
    using System.Threading.Tasks; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
      Task.Factory.StartNew(() => 
      { 
       Console.WriteLine("Task started."); 
       throw new Exception("Test Exception"); 
      }); 

      Thread.Sleep(1000); 
      Console.WriteLine("First Collect"); 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      Console.WriteLine("Waiting"); 
      Console.ReadKey(); 
     } 

     static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
     { 
      Console.WriteLine("EXCEPTION UNOBSERVED"); 
     } 
    } 
} 

me he dado cuenta de que el depurador a menudo "trampas" del evento UnobservedTaskException, haciendo que se dispara de manera apropiada. Ejecute este fuera del depurador, e imprimirá "EXCEPCIÓN UNOBSERVED" cada vez que se apaga.

+0

Hola, gracias por la sugerencia, aunque en realidad ya la estoy ejecutando fuera del depurador.También noto que estás usando GC.Collect(). ¿Qué sucede en tu ejemplo si no recolectas manualmente? –

+0

@Nathan: Normalmente, no hay suficiente presión de GC para que esto suceda en este simple ejemplo. Si el GC no se recopila, la "tarea" no llegará al finalizador, y la excepción no observada nunca se activará. Solo ocurre cuando el finalizador recopila una tarea no rooteada con una excepción. –

+0

@ Nathan: Tenga en cuenta que UnobservedTaskException no evitará que la aplicación se cierre en ningún caso; necesita manejar sus excepciones dentro de su tarea, o siempre esperar en una tarea, si desea evitar que esto cierre una aplicación. –

5

Para mí, TaskScheduler.UnobservedTaskException al principio da una sensación de seguridad muy incorrecta. Realmente no vale mucho si depende de la recolección de basura.

He encontrado la siguiente solución, tomada de this msdn article, para ser mucho más confiable. Básicamente ejecuta el bloque de continuación (donde registra la excepción) solo si hubo excepciones no controladas en la tarea 1 y no bloquea la ejecución de la UI.

También es posible que desee aplanar las AggregateExceptions anidadas y quizás crear un método de extensión, como Reed Copsey depicted here.

var task1 = Task.Factory.StartNew(() => 
{ 
    throw new MyCustomException("Task1 faulted."); 
}) 
.ContinueWith((t) => 
{ 
    Console.WriteLine("I have observed a {0}", 
     t.Exception.InnerException.GetType().Name); 
}, 
TaskContinuationOptions.OnlyOnFaulted); 
Cuestiones relacionadas