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

Я полностью озадачен. Я был так уверен, что .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);

это делает асинхронный вызов, который создает другой поток для выполнения этого