Как предотвратить исключение приложения в фоновом потоке из приложения? - PullRequest
41 голосов
/ 09 октября 2008

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

Ответы [ 5 ]

38 голосов
/ 09 октября 2008

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

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

Возвращаясь к .Net 1.1, необработанное исключение в фоновом потоке просто было бы выброшено в никуда, и основной поток с удовольствием начал бы пахать. И это может иметь неприятные последствия. Так что в .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

9 голосов
/ 09 октября 2008

От превосходного Джо Албахари статья:

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

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

3 голосов
/ 29 июня 2009

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

Вот демоверсия обходного пути:

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();

1 голос
/ 09 октября 2008

Вот отличное сообщение в блоге об этой проблеме: Обработка «необработанных исключений» в .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));
}
0 голосов
/ 24 декабря 2017
    AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
    {
        Thread.CurrentThread.Join();
    };

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...