Возможно ли не запустить триггер необработанного исключения AppDomain из продолжения Задачи? - PullRequest
3 голосов
/ 10 сентября 2011

У меня есть Task, который работает асинхронно и обрабатывает исключения, используя продолжение задачи task.ContinueWith(t => ..., CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, taskScheduler).

Это прекрасно работает для обработки исключений, которые я знаю, как обрабатывать (например, WebException), но что, если выдается что-то вроде NullReferenceException, которое я не могу обработать правильно?

Я хочу иметь возможность обрабатывать его, используя необработанный обработчик исключений AppDomain (который в нашем случае регистрирует ошибку, выскакивает диалоговое окно с уведомлением пользователя и выходит из приложения), но до сих пор я не нашел хорошего способа сделать это.

Простое перебрасывание исключения из продолжения приводит к тому, что обработчик необработанного исключения вызывается при завершении задачи (поскольку переброшенное исключение не наблюдается). Это не очень хорошо, потому что могут пройти секунды или даже минуты после того, как я узнаю, что приложение должно аварийно завершиться.

Вызов Environment.FailFast() приводит к сбою приложения, но не вызывает необработанный обработчик исключений.

Вызов Thread.CurrentThread.Abort() вызывает обработчик необработанного исключения, но отображает только информацию / трассировку стека из ThreadAbortException. Кроме того, использование этого метода кажется плохой идеей.

Вызов Application.OnThreadException() фактически делает именно то, что я хочу, за исключением того, что он требует ссылки System.Windows.Forms и обработки Application.ThreadException, которая не будет работать, например, со службой без пользовательского интерфейса.

Звонить по номеру task.Wait() не имеет смысла, потому что в нашем случае это некуда звонить. Если задача выполнена успешно, результат обрабатывается с использованием продолжения успеха, если он терпит неудачу, вызывается продолжение исключения, и мы не можем заблокировать поток, создающий задачу (в этом весь смысл задачи).

Я мог бы вызывать обработчик необработанных исключений напрямую, за исключением того, что по крайней мере в одном случае потребовалось бы добавить зависимость, которую я не хочу добавлять в свое приложение.

Мне не хватает очевидного способа сделать это?

Ответы [ 3 ]

1 голос
/ 10 сентября 2011

Я не нашел ни одного класса BCL, который бы выполнял подобную работу.Итак, я могу предложить вам использовать некоторый рукописный статический класс:

static class Crusher
{
    private static readonly AutoResetEvent CrushEvent;
    private static Exception _exception;

    static Crusher()
    {
        CrushEvent = new AutoResetEvent(false);
    }

    public static Thread GetCrushWaitingThread()
    {
        return new Thread(WaitForCrush);
    }

    static void WaitForCrush()
    {
        CrushEvent.WaitOne();
        throw _exception;
    }

    public static void Crush(Exception exception)
    {
        _exception = exception;
        CrushEvent.Set();
    }
}

Вы должны просто инициализировать его при запуске приложения:

static void Main(string[] args)
{
    AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
    Crusher.GetCrushWaitingThread().Start();
    ...

, а затем - вы можете вызвать егоиз продолжений:

task.ContinueWith(Continuation, TaskContinuationOptions.OnlyOnFaulted);

...

private static void Continuation(Task obj)
{
    Crusher.Crush(obj.Exception);
}

Но это не так приятно, как реальное переброшенное необработанное исключение (оно содержит дополнительную информацию о методе Crusher.WaitForCrush() в stacktrace).

0 голосов
/ 03 декабря 2012

Просто используйте ту же логику, которую вы реализовали для AppDomain.UnhandledException, и вызывайте ее явно, когда вы нажмете исключение в своем продолжении.Вот пример обработчика сбоев, который я использую:

public static class CrashHandler
{
    public static void Setup()
    {
        AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
    }

    public static Task CrashOnUnhandledException(this Task t)
    {
        t.ContinueWith(x => HandleException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
        return t;
    }

    private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        HandleException(e.ExceptionObject);
    }

    private static void HandleException(object ex)
    {
        ShowErrorMessage(ex);

        Environment.Exit(-1);
    }

    private static void ShowErrorMessage(object ex)
    {
        // your crash dialog goes here
    }
}

Метод расширения CrashOnUnhandledException можно использовать следующим образом, чтобы сэкономить некоторую печать и дать дополнительную информацию о сайте вызовов в трассировке стека:

task.ContinueWith(t => { /* your logic */ })
    .CrashOnUnhandledException();
0 голосов
/ 21 февраля 2012

Мы использовали Application.OnThreadException(), чтобы решить наш конкретный случай.

...