Я могу подключиться к AppDomain.CurrentDomain.UnhandledException
для регистрации исключений из фоновых потоков, но как я могу предотвратить их завершение среды выполнения?
Как предотвратить исключение в фоновом потоке от завершения приложения?
Ответ 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 должна позволить разработчикам переопределять логику необработанных исключений из верхней части стека.