2010-07-19 20 views
21

Estoy leyendo un libro sobre la Biblioteca paralela de tareas C# y tengo el siguiente ejemplo, pero el controlador TaskScheduler.UnobservedTaskException nunca se desencadena. ¿Alguien puede darme alguna pista sobre por qué?TaskScheduler.UnobservedTaskException controlador de eventos nunca se desencadena

TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
{ 
    eventArgs.SetObserved(); 
    ((AggregateException)eventArgs.Exception).Handle(ex => 
    { 
     Console.WriteLine("Exception type: {0}", ex.GetType()); 
     return true; 
    }); 
}; 

Task task1 = new Task(() => 
{ 
    throw new ArgumentNullException(); 
}); 

Task task2 = new Task(() => { 
    throw new ArgumentOutOfRangeException(); 
}); 

task1.Start(); 
task2.Start(); 

while (!task1.IsCompleted || !task2.IsCompleted) 
{ 
    Thread.Sleep(5000); 
} 

Console.WriteLine("done"); 
Console.ReadLine(); 
+3

¿Qué libro es ese? –

+0

Sería realmente curioso, el ejemplo es incorrecto, ya que es imposible que el evento se plantee en este ejemplo ... –

+1

Este es el libro: http://www.apress.com/book/view/1430229675 Pro .NET 4 Programación paralela en C# – devlife

Respuesta

34

Desafortunadamente, ese ejemplo nunca le mostrará su código. El UnobservedTaskException solo ocurrirá si el GC recolecta una tarea con una excepción no observada: siempre que mantenga una referencia a task1 y task2, el GC nunca se acumulará, y nunca verá su manejador de excepciones.

Con el fin de ver el comportamiento de la UnobservedTaskException en acción, me gustaría probar el siguiente (ejemplo artificioso):

public static void Main() 
{ 
    TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
       { 
        eventArgs.SetObserved(); 
        ((AggregateException)eventArgs.Exception).Handle(ex => 
        { 
         Console.WriteLine("Exception type: {0}", ex.GetType()); 
         return true; 
        }); 
       }; 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentNullException(); 
    }); 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentOutOfRangeException(); 
    }); 


    Thread.Sleep(100); 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    Console.WriteLine("Done"); 
    Console.ReadKey(); 
} 

Esto le mostrará sus mensajes. La primera llamada Thread.Sleep(100) proporciona suficiente tiempo para lanzar las tareas. El recoger y esperar fuerza una colección GC, que disparará su controlador de eventos 2x.

+0

Eso lo explica. ¡Gracias! – devlife

+6

En .NET 4.5, necesita vudú adicional para que esto suceda. En primer lugar, debe habilitar [ThrowUnobservedTaskException] (http://msdn.microsoft.com/en-GB/library/jj160346.aspx) en app.config, y segundo, debe compilar el código en la configuración de Release (un punto que solo está documentado en la sección de Ejemplo del artículo enlazado de MSDN). Culpo a esta mentalidad de "vamos a suprimir las excepciones silenciosamente" en JavaScript en los navegadores :) –

+2

@romkyns, no necesitaba hacer esto para mi aplicación .NET 4.5. –

3

La excepción no será "no observada" en ese fragmento de muestra. No hasta que el recolector de basura se deshaga de las instancias de la tarea. Habría que volver a escribir así:

class Program { 
    static void Main(string[] args) { 

     TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
     { 
      eventArgs.SetObserved(); 
      ((AggregateException)eventArgs.Exception).Handle(ex => 
      { 
       Console.WriteLine("Exception type: {0}", ex.GetType()); 
       return true; 
      }); 
     }; 

     Run(); 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     Console.WriteLine("done"); 
     Console.ReadLine(); 
    } 

    static void Run() { 
     Task task1 = new Task(() => { 
      throw new ArgumentNullException(); 
     }); 

     Task task2 = new Task(() => { 
      throw new ArgumentOutOfRangeException(); 
     }); 

     task1.Start(); 
     task2.Start(); 

     while (!task1.IsCompleted || !task2.IsCompleted) { 
      Thread.Sleep(50); 
     } 
    } 
} 

No ello, utilice Task.Wait().

0

corro este código en .NET 4.5, Visual Studio 2012 (depuración o la liberación, no importa), no lo hice poner ThrowUnobservedTaskException en mi app.config:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Test 
{ 
    public static class Program 
    { 
     private static void Main() 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 

      RunTask(); 

      Thread.Sleep(2000); 

      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 

      Console.ReadLine(); 
     } 

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

     private static void RunTask() 
     { 
      Task<int> task = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); // emulate some calculation 
       Console.WriteLine("Before exception"); 
       throw new Exception(); 
       return 1; 
      }); 
     } 
    } 
} 

Y la excepción es capturado por el controlador UnobservedTaskException (se imprime "Caught!").

+0

Esto se debe a 'ThrowUnobservedTaskException 'solo es necesario si desea volver al comportamiento 4.0 de una excepción de Tarea que finaliza el proceso. En 4.5, el valor predeterminado cambió a * no * finaliza el proceso. No es necesario que la excepción no observada en su ejemplo sea capturada por el controlador de eventos. Más información en las observaciones [aquí] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception% 28v = vs.110% 29.aspx) – BitMask777

Cuestiones relacionadas