without run method example ejemplos await async c# .net exception task-parallel-library async-await

c# - run - Controlador de excepción global TAP



task c# example (5)

Bueno, ¿cómo definirías un manejador global de aplicaciones para lidiar con una excepción en este caso?

string x = DoSomething();

Es probable que la respuesta a su pregunta sea exactamente la misma. Parece que está esperando correctamente un método asíncrono, y el compilador hace todo lo posible para asegurarse de que cualquier excepción que se produzca en el método asincrónico se propague y desenrolle de una manera que le permita manejarlo como lo haría en el código síncrono. Este es uno de los principales beneficios de async / await.

Este código arroja una excepción. ¿Es posible definir un controlador global de aplicación que lo atrape?

string x = await DoSomethingAsync();

Usando .net 4.5 / WPF


EDITADO según el comentario de @Noseration

En .NET 4.5 en código async , puede manejar excepciones no observadas registrando un controlador para el evento TaskScheduler.UnobservedTaskException . Se considera que una excepción no se observa si no se accede a las propiedades Task.Result , Task.Exception y no se llama a Task.Wait .

Después de que la excepción no observada llega al controlador de eventos TaskScheduler.UnobservedTaskException , el comportamiento predeterminado es tragar esta excepción para que el programa no falle. Este comportamiento se puede cambiar en el archivo de configuración agregando lo siguiente:

<configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration>


Esta es realmente una buena pregunta, si lo entendí correctamente. Inicialmente voté para cerrarlo, pero ahora se retractó de mi voto.

Es importante comprender cómo una excepción arrojada dentro de un método de async Task se propaga fuera de él. Lo más importante es que dicha excepción debe ser observada por el código que maneja la finalización de la tarea.

Por ejemplo, aquí hay una aplicación WPF simple, estoy en NET 4.5.1:

using System; using System.Threading.Tasks; using System.Windows; namespace WpfApplication_22369179 { public partial class MainWindow : Window { Task _task; public MainWindow() { InitializeComponent(); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; _task = DoAsync(); } async Task DoAsync() { await Task.Delay(1000); MessageBox.Show("Before throwing..."); GCAsync(); // fire-and-forget the GC throw new ApplicationException("Surprise"); } async void GCAsync() { await Task.Delay(1000); MessageBox.Show("Before GC..."); // garbage-collect the task without observing its exception _task = null; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); } void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { MessageBox.Show("TaskScheduler_UnobservedTaskException:" + e.Exception.Message); } void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { MessageBox.Show("CurrentDomain_UnhandledException:" + ((Exception)e.ExceptionObject).Message); } } }

Una vez que se ha lanzado ApplicationException , no se observa. Ni TaskScheduler_UnobservedTaskException ni CurrentDomain_UnhandledException se invocan. La excepción permanece inactiva hasta que el objeto _task sea ​​esperado o esperado. En el ejemplo anterior, nunca se observa, por TaskScheduler_UnobservedTaskException que se invoca a TaskScheduler_UnobservedTaskException solo cuando la tarea se recolecta . Entonces esta excepción será tragada .

El antiguo comportamiento .NET 4.0, donde el evento AppDomain.CurrentDomain.UnhandledException se activa y la aplicación falla, se puede habilitar configurando ThrowUnobservedTaskExceptions en app.config :

<configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration>

Cuando se habilita de esta manera, AppDomain.CurrentDomain.UnhandledException seguirá TaskScheduler.UnobservedTaskException después de TaskScheduler.UnobservedTaskException cuando la excepción se recolecta basura, en lugar de en el lugar donde se lanzó.

Este comportamiento lo describe Stephen Toub en su publicación de blog "Task Exception Handling in .NET 4.5" . La parte sobre la recolección de basura de tareas se describe en los comentarios a la publicación.

Ese es el caso con los métodos de async Task . La historia es bastante diferente para los métodos async void , que normalmente se usan para manejadores de eventos. Cambiemos el código de esta manera:

public MainWindow() { InitializeComponent(); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; this.Loaded += MainWindow_Loaded; } async void MainWindow_Loaded(object sender, RoutedEventArgs e) { await Task.Delay(1000); MessageBox.Show("Before throwing..."); throw new ApplicationException("Surprise"); }

Debido a que es un async void no hay referencia de Task a la que aferrarse (por lo que no se observará nada o se recolectará basura más adelante). En este caso, la excepción se lanza inmediatamente en el contexto de sincronización actual. Para una aplicación WPF, Dispatcher.UnhandledException activará primero, luego Application.Current.DispatcherUnhandledException , luego AppDomain.CurrentDomain.UnhandledException . Finalmente, si no se maneja ninguno de estos eventos ( EventArgs.Handled no se establece en true ), la aplicación se bloqueará, independientemente de la configuración ThrowUnobservedTaskExceptions . TaskScheduler.UnobservedTaskException no se TaskScheduler.UnobservedTaskException en este caso, por el mismo motivo: no hay Task .


Al vincular un evento a AppDomain.CurrentDomain.FirstChanceException se garantiza que su excepción se detectará. Como señaló @Noseratio, se le notificará cada excepción en su aplicación, incluso si la excepción se maneja correctamente dentro de un bloque catch y la aplicación continúa.

Sin embargo, todavía veo que este evento es útil para al menos capturar las últimas excepciones lanzadas antes de que se detenga una aplicación o quizás algún otro escenario de depuración.

Si quieres protegerte contra esto

string x = await DoSomethingAsync();

Mi consejo es que no hagas eso, agrega un bloque try catch :-)


Siempre puede hacer lo siguiente para manejar la excepción utilizando el método Application.DispatcherUnhandledException . Por supuesto, se te daría dentro de una TargetInvocationException y podría no ser tan bonita como otros métodos. Pero funciona perfectamente bien

_executeTask = executeMethod(parameter); _executeTask.ContinueWith(x => { Dispatcher.CurrentDispatcher.Invoke(new Action<Task>((task) => { if (task.Exception != null) throw task.Exception.Flatten().InnerException; }), x); }, TaskContinuationOptions.OnlyOnFaulted);