Подтвердить что ты не робот

Как предотвратить исключение в фоновом потоке от завершения приложения?

Я могу подключиться к AppDomain.CurrentDomain.UnhandledException для регистрации исключений из фоновых потоков, но как я могу предотвратить их завершение среды выполнения?

4b9b3361

Ответ 1

Во-первых, вы действительно должны стараться не иметь исключений, брошенных - и не обрабатываемых - в фоновом потоке. Если вы контролируете способ выполнения вашего делегата, инкапсулируйте его в блок catch try и укажите способ передачи информации об исключении обратно в ваш основной поток (используя EndInvoke, если вы явно вызываете BeginInvoke или обновляете какое-то общее состояние где-то).

Игнорирование необработанного исключения может быть опасным. Если у вас есть реальное исключающее исключение (исключение OutOfMemoryException), вы ничего не можете сделать, и ваш процесс в основном обречен.

Вернемся к .Net 1.1, необработанное исключение в backgroundthread просто будет выброшено в никуда, и основной поток с удовольствием начнет пахать. И это может привести к неприятным последствиям. Таким образом, в .Net 2.0 это поведение изменилось.

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

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

Просто добавьте этот параметр в свой сервис/приложение/любой конфигурационный файл:

<configuration>
  <runtime>
    <!-- the following setting prevents the host from closing when an unhandled exception is thrown -->
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

Однако он не работает с ASP.NET.

Для получения дополнительной информации (и огромного предупреждения о том, что этот параметр может не поддерживаться в будущих версиях CLR) см. http://msdn.microsoft.com/en-us/library/ms228965.aspx

Ответ 2

От Джо Альбахари отлично threading статья:

Структура .NET обеспечивает событие более низкого уровня для глобального исключения обработка: AppDomain.UnhandledException. Эта событие срабатывает при необработанном исключение в любом потоке и в любом тип приложения (с или без пользовательский интерфейс). Однако, хотя это предлагает хороший механизм последнего действия для регистрации исключенных исключений не препятствует приложения от выключения - и нет средств для подавления .NET. диалог необработанных исключений.

В производственных приложениях явно обработка исключений требуется для всех методы ввода потоков. Можно разрезать работать с помощью обертки или помощника класс для выполнения задания, например BackgroundWorker (обсуждается в Части 3).

Ответ 3

Сохраняя короткий ответ, да, вы можете предотвратить завершение выполнения.

Ниже приведено описание обходного пути:

class Program
{
    void Run()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Console.WriteLine("Press enter to exit.");

        do
        {
            (new Thread(delegate()
            {
                throw new ArgumentException("ha-ha");
            })).Start();

        } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");


        Console.WriteLine("last good-bye");
    }

    int r = 0;

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Interlocked.Increment(ref r);
        Console.WriteLine("handled. {0}", r);
        Console.WriteLine("Terminating " + e.IsTerminating.ToString());

        Thread.CurrentThread.IsBackground = true;
        Thread.CurrentThread.Name = "Dead thread";            

        while (true)
            Thread.Sleep(TimeSpan.FromHours(1));
        //Process.GetCurrentProcess().Kill();
    }

    static void Main(string[] args)
    {
        Console.WriteLine("...");
        (new Program()).Run();
    }
}

По существу, вы просто не позволяли среде выполнения показывать диалог "... программа перестала работать".

Если вам нужно зарегистрировать исключение и выйти из него, вы можете вызвать Process.GetCurrentProcess().Kill();

Ответ 4

Вот отличная запись в блоге об этой проблеме: Обработка "необработанных исключений" в .NET 2.0

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

delegate void ExceptionCallback(Exception ex);

void MyExceptionCallback(Exception ex)
{
   throw ex; // Handle/re-throw if necessary
}

void BackgroundThreadProc(Object obj)
{
   try 
   { 
     throw new Exception(); 
   }
   catch (Exception ex)
   { 
     this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); 
   }
}

private void Test()
{
   ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc));
}

Ответ 5

    AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
    {
        Thread.CurrentThread.Join();
    };

Но будьте осторожны, этот код затормозит всю стек стека для объекта Thread и thread managed. Однако, если ваше приложение находится в определенном состоянии (возможно, вы выбрали исключение LimitedDemoFunctionalityException или OperationStopWithUserMessageException), и вы не разрабатываете приложение 24/7, этот трюк будет работать.

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