4

В результате изучения этого вопроса: Rethrowing exception in Task doesn't make the Task to go to faulted state, я заметил очень странное поведение с ThreadAbortException, о котором я не могу понять.Нечеткое поведение: Захват ThreadAbortException и исключение различного исключения

Теперь я знаю, что ThreadAbortException - это особый вид исключения для начала. И documentation довольно четкое представление о том, что, когда он говорит:

ThreadAbortException является исключением, которое может быть пойман, но он автоматически будет вновь поднят в конце catch блока.

Сценарий № 1: Документированное поведение.

static void Main(string[] args) 
{ 
    try 
    { 
     Thread.CurrentThread.Abort(); 
    } 
    catch (Exception tae) 
    { 
     Console.WriteLine("caught exception: " + tae.GetType().Name); 
    } 
    Console.WriteLine("will never be reached"); 
} 

Как и ожидалось, ThreadAbortException будет вызваны повторно автоматически, в результате чего следующий вывод:

caught exception: ThreadAbortException 

Сценарий № 2: Где он получает интересное, когда я решил бросить другое исключение в catch блок:

static void Main(string[] args) 
{ 
    try 
    { 
     Thread.CurrentThread.Abort(); 
    } 
    catch (Exception tae) 
    { 
     Console.WriteLine("caught exception: " + tae.GetType().Name); 
     throw new ApplicationException(); // will ThreadAbortException take precedence? 
    } 
    Console.WriteLine("will never be reached"); 
} 

В этом случае, я предположил, что, несмотря на метания ApplicationException, что в любом случае будет восстановлено значение ThreadAbortException, чтобы гарантировать, что документальное поведение было сохранено. К моему удивлению, это выход, который привел:

caught exception: ThreadAbortException 

Unhandled Exception: System.ApplicationException: Error in the application. 
    at ConsoleApplication1.Program.Main(String[] args) in C:\projects\ConsoleApplication1\Program.cs:line 193 

ли ApplicationException фактически заменить и предотвратитьThreadAbortException от броска?!?

Сценарий № 3: И, наконец, чтобы сделать дела более интересными, я обернуть обработки моего существующего исключения с еще одним слоем try-catch:

static void Main(string[] args) 
{ 
    try 
    { 
     try 
     { 
      Thread.CurrentThread.Abort(); 
     } 
     catch (Exception tae) 
     { 
      Console.WriteLine("caught exception: " + tae.GetType().Name); 
      throw new ApplicationException(); 
     } 
    } 
    catch (Exception outerEx) 
    { 
     Console.WriteLine("caught outer exception: " + outerEx.GetType().Name); 
    } 
    Console.WriteLine("will never be reached"); 
} 

Теперь я не слишком уверен, что ожидать. Какое исключение будет обнаружено во внешнем блоке catch? Будет ли это ApplicationException? И если да, значит ли это, что я смогу проглотить исключение и действительно удастся распечатать строку will never be reached?

Это фактический выход:

caught exception: ThreadAbortException 
caught outer exception: ApplicationException 

Из вышеприведенного вывода, это выглядит как внешний catch блок на самом деле поймать ApplicationException. Но к концу , чтоcatch блок, ThreadAbortException теперь внезапно появляется из воздуха и возвращается?

Вопрос (и): Может ли кто-нибудь объяснить и согласовать сценарии № 2 и № 3? Как это происходит в сценарии №2, похоже, что ThreadAbortException неожиданно заменен другим исключением? Тем не менее, в сценарии № 3, похоже, что ThreadAbortException все еще был там? Как это происходит?Это где-то документировано?

+0

Еще более заинтересован в подключении к событию «AppDomain.UnhandledException» - он срабатывает, даже если вы обрабатываете его, как и следовало ожидать, но в противном случае вы не получите поведение по умолчанию. – jdphenix

+0

https://gist.github.com/jdphenix/32a710ad5def12cf121c - Запустите это, наглядно демонстрируя, что 'ThreadAbortException' продолжает распространяться. У меня нет объяснений, почему обработчик исключенных по умолчанию по умолчанию не захватывает его. – jdphenix

+0

@jdphenix: попробуйте прокомментировать «try-catch» в вашем методе 'HandleApplicationException (Action action)' и посмотреть, какое исключение попало в обработчик события AppDomain.UnhandledException. Обратите внимание, что в этом случае похоже, что «ThreadAbortException» не распространялся, а вместо этого «ApplicationException» распространяется. Очень странно. – sstan

ответ

1

Поведение специфично для реализации.

От Ecma-335 VI Annex E, Portability Considerations:

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

и далее (курсив мой):

V I. E. 1 Неконтролируемое поведение

Следующие аспекты поведения программы зависят от реализации. > Многие из этих элементов будут знакомы программистам, используемым для написания кода, предназначенного для> переносимости (например, факта, что CLI не налагает минимальный размер для кучи или стека).

  1. Размер кучи и стека не должны иметь минимальные размеры

Поведение относительно асинхронных исключений (см System.Thread.Abort)

Глобализация не поддерживается, так каждая реализация определяет свою информацию о культуре, включая такие видимые пользователем функции, как порядок сортировки для строк.

Нити не могут быть преднамеренно или не преднамеренно запланированы. Это решение является специфическим для реализации.

Расположение сборок является механизмом, специфичным для реализации.

Политика безопасности - это специфический для реализации механизм.

Имена файлов специфичны для реализации.

Разрешение таймера (гранулярность) является специфичным для реализации, хотя устройство указано.