Почему необработанное исключение в фоновом потоке не разрушает домен приложения?
Я полностью озадачен. Я был так уверен, что .NET закрывает весь домен приложения, если в потоке есть неперехваченное исключение, которое я никогда не тестировал.
Однако я просто попробовал следующий код, и он не подводит... Может ли кто-нибудь объяснить, почему?
(Пробовал в .NET 4 и 3.5)
static void Main(string[] args)
{
Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);
Action a = new Action(() =>
{
Console.WriteLine("Background thread {0}", Thread.CurrentThread.ManagedThreadId);
throw new ApplicationException("test exception");
});
a.BeginInvoke(null, null);
Console.ReadLine();
}
Ответы
Ответ 1
Это происходит потому, что BeginInvoke
использует ThreadPool
внутренне, и когда ThreadPool
любые нерассмотренные исключения будут терпеть молчание. Однако, если вы используете a.EndInvoke
, то исключенное исключение будет выбрано методом EndInvoke
.
Примечание: в качестве João Angelo
указано, что использование методов ThreadPool
, непосредственно "как ThreadPool.QueueUserWorkItems
и UnsafeQueueUserWorkItem
", будет генерировать исключения в 2.0 и выше.
Ответ 2
Из Исключения в управляемых потоках в MSDN:
В .NET Framework версии 2.0 общее время выполнения необработанные исключения в потоках Естественно. В большинстве случаев это означает, что необработанное исключение приводит к завершению работы приложения.
Это существенное изменение .NET Framework версии 1.0 и 1.1, которые обеспечивают обратный механизм для многих необработанные исключения - например, необработанные исключения в пуле потоков потоки. См. Изменение с предыдущего Версии позже в этом разделе.
В качестве меры временной совместимости, администраторы могут флаг совместимости в раздел приложения Файл конфигурации. Это вызывает время выполнения общего языка для возврата к поведение версий 1.0 и 1.1.
<legacyUnhandledExceptionPolicy enabled="1"/>
Ответ 3
Обычно с асинхронными делегатами, если делегированный метод генерирует исключение, поток прерывается, и исключение будет вызываться снова в вызывающем коде только, когда вы вызываете EndInvoke
.
Вот почему при использовании асинхронного делегата (BeginInvoke
) вы всегда должны звонить EndInvoke
. Кроме того, это не следует путать с Control.BeginInvoke
, который можно вызвать в режиме огня и забыть.
Раньше я говорил нормально, потому что есть возможность заявить, что исключение следует игнорировать, если метод delegate возвращает void. Для этого вам нужно пометить метод атрибутом OneWay
.
Если вы запустите следующий пример, вы получите только исключение при вызове willNotIgnoreThrow.EndInvoke
.
static void Throws()
{
Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);
throw new ApplicationException("Test 1");
}
[OneWay]
static void ThrowsButIsIgnored()
{
Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId);
throw new ApplicationException("Test 2");
}
static void Main(string[] args)
{
Console.WriteLine("Main: {0}", Thread.CurrentThread.ManagedThreadId);
var willIgnoreThrow = new Action(ThrowsButIsIgnored);
var result1 = willIgnoreThrow.BeginInvoke(null, null);
Console.ReadLine();
willIgnoreThrow.EndInvoke(result1);
Console.WriteLine("============================");
var willNotIgnoreThrow = new Action(Throws);
var result2 = willNotIgnoreThrow.BeginInvoke(null, null);
Console.ReadLine();
willNotIgnoreThrow.EndInvoke(result2);
}
Ответ 4
потому что исключение броска на данные потоки остается там, если оно не направлено обратно в основной поток.
То, что backgroundWorker делает для вас, если у вас есть исключение в потоке BackgroundWorker, оно возвращается к основному потоку.
a.BeginInvoke(null, null);
это делает асинхронный вызов, который создает другой поток для выполнения этого