38

Estoy usando el TPL (Task Parallel Library) en .NET 4.0. Quiero poder centralizar la lógica de manejo de todas las excepciones no controladas utilizando el evento Thread.GetDomain().UnhandledException. Sin embargo, en mi aplicación, el evento nunca se activa para los hilos iniciados con el código TPL, p. Task.Factory.StartNew(...). El evento de hecho se dispara si utilizo algo como new Thread(threadStart).Start().¿Cómo manejar todas las excepciones no controladas al usar Task Parallel Library?

This MSDN article sugiere utilizar la tarea # Wait() para capturar el AggregateException cuando se trabaja con TPL, pero no es lo que quiero porque no es un mecanismo "centralizado".

¿Alguien tiene el mismo problema o soy yo? ¿Tienes alguna solución para esto?

Respuesta

21

Parece que no hay una forma integrada de manejar esto (y no hay respuesta a esta pregunta después de casi 2 semanas). Ya saqué un código personalizado para encargarme de esto. La descripción de la solución es bastante larga, así que la publiqué en mi blog. Consulte this post si le interesa.

Actualización 5/7/2010: He encontrado una mejor manera de hacerlo, haciendo uso de la continuación de la tarea. Creo un class ThreadFactory que expone el evento de error que puede ser suscrito por un controlador de nivel superior y proporciona métodos para iniciar una tarea adjunta con la continuación adecuada.
El código se publica here.

Actualización 18/04/2011: Código postal de la publicación del blog según el comentario de Nifle.

internal class ThreadFactory 
{ 
    public delegate void TaskError(Task task, Exception error); 

    public static readonly ThreadFactory Instance = new ThreadFactory(); 

    private ThreadFactory() {} 

    public event TaskError Error; 

    public void InvokeError(Task task, Exception error) 
    { 
     TaskError handler = Error; 
     if (handler != null) handler(task, error); 
    } 

    public void Start(Action action) 
    { 
     var task = new Task(action); 
     Start(task); 
    } 

    public void Start(Action action, TaskCreationOptions options) 
    { 
     var task = new Task(action, options); 
     Start(task); 
    } 

    private void Start(Task task) 
    { 
     task.ContinueWith(t => InvokeError(t, t.Exception.InnerException), 
          TaskContinuationOptions.OnlyOnFaulted | 
          TaskContinuationOptions.ExecuteSynchronously); 
     task.Start(); 
    } 
} 
+1

La solución actualizada funciona muy bien por lo que yo sé. ¿Por qué tan poca gente tiene este problema? –

+0

Sería muy amable de su parte si pudiera incluir el código de su blog aquí también. – Nifle

+0

@Buu Nguyen Hola, hice algo basado en su enfoque aquí: http://stackoverflow.com/questions/11831844/unobservedtaskexception-being-throw-but-it-is-handled-by-a-taskscheduler-unobser/11908212 # 11908212 Muchas gracias. Esperando que C# tenga algo mejor. – newway

32

creo TaskScheduler.UnobservedTaskException Event es lo que quiere:

Ocurre cuando un fallo Task's excepción no observada está a punto de desencadenar la directiva de extensión excepción, que, por defecto, se terminará el proceso .

Por lo tanto, este evento es similar al DomainUnhandledException que mencionó en su pregunta, pero se produce solo para las tareas.

Por cierto, esa política de excepciones no observadas (sí, esto no es una excepción no observada, MS chicos inventaron una nueva palabra ... otra vez), cambió de .NET 4.0 a .NET 4.5. En .NET 4.0 excepción no observada lleva a la terminación del proceso, pero en .NET 4.5 - no. Esto es todo porque las nuevas cosas asincrónicas que tendremos en C# 5 y VB 11.

+0

Gracias, Sergey. Soy consciente de ello, pero por alguna razón no funcionó para mí http://www.buunguyen.net/blog/handle-all-uncaught-exceptions-thrown-when-using-task-parallel-library. html # comment-7259 –

+8

El problema es que el evento 'UnobservedTaskException' solo se genera cuando se finaliza el objeto 'Tarea' fallado. Y la finalización del objeto simplemente no está garantizada para ejecutarse. –

+0

Mención realmente útil de que la política de excepción no observada ha cambiado en .NET 4.5 – stukselbax

10

Veo dos opciones que se pueden utilizar con el propósito de centralizar el manejo de excepciones en TPL: 1. Uso de excepción de tarea no observada evento del Programador de tareas. 2. Uso de continuaciones para tareas con estado en falla.

Uso del evento de excepción de tarea no observada del Programador de tareas.

El planificador de tareas tiene un evento UnobservedTaskException al que puede suscribirse mediante el operador + =.

  • Nota 1: En el cuerpo del controlador que necesita para hacer llamadas SetObserved() el argumento UnobservedTaskExceptionEventArgs para notificar planificador que se manejó excepción.
  • Nota 2: Se llama al controlador cuando el recolector de basura ha recopilado las tareas.
  • Nota 3: Si espera en la tarea, se verá obligado a proteger la espera en el bloque de prueba/captura.
  • Nota 4: La política predeterminada para excepciones de tareas no controladas en .Net 4.0 y 4.5 es diferente.

Resumen: Este enfoque es bueno para tareas de "olvídate del fuego" y para detectar excepciones escapadas de tu política centralizada de manejo de excepciones.

Uso de continuaciones para tareas con estado en fallo.

Con TPL puede adjuntar acciones a la tarea mediante el método ContinueWith() que toma la acción de adjuntar y la opción de continuación. Se llamará a esta acción después de la terminación de la tarea y solo en los casos especificados por opción. En particular:

t.ContinueWith(c => { /* exception handling code */ }, TaskContinuationOptions.OnlyOnFaulted); 

instala continuación con el código de control de excepciones a la tarea t. Este código se ejecutará solo en el caso cuando finalizó la Tarea t debido a la excepción no controlada.

  • Nota 1: Obtenga el valor de excepción en el código de excepción. De lo contrario, se borrará.
  • Nota 2: Se llamará al código de manejo de excepciones inmediatamente después de la finalización de la tarea.
  • Nota 3: Si la excepción se obtuvo en el código de manejo de excepciones, se considerará como manejada, el bloque try/catch en la tarea en espera no podrá detectarlo.

Creo que será mejor para el manejo de excepciones centralizado utilizar Tareas personalizadas heredadas de Tarea con el controlador de excepciones agregado a través de la continuación. Y acompañe este enfoque utilizando el evento Excepción de tarea no observada del Programador de tareas para detectar intentos de utilizar tareas no personalizadas.

Cuestiones relacionadas