Что не может поймать try/catch и как с этим бороться?

Что не может поймать try/catch ?

И если он не может - может быть что то другое может ?

Был у меня такой вопрос: Необработанное исключение типа "System.AggregateException" в System.Private.CoreLib.dll

try/catch тут несправляется, хотя мне казалось что он ловит всё...

Я как то задавал вопрос касательно этого поведения, но не хватало StackTrace и меня убедили что я нехорошее слово забыл где то await использовать, поэтому и валилось всё из за AggregareExeption

И до сегодняшнего момента я думал что косяк у меня, который просто не мог найти и я с этим боролся перезапуская приложение.

Вот как можно обрабатывать даже такие ошибки ?

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

введите сюда описание изображения


Ответы (1 шт):

Автор решения: aepot

Перехват исключений выглядит примерно так:

  • Блок try
  • Вызов дочернего метода
  • Вызов ещё одного метода
  • Выброс исключения в дочерний метод
  • Продолжения выброса исключений в блок try
  • Срабатывание catch

То есть вызовы идут вглубь, а исключение возвращается по стеку вызовов назад используя историю вызовов как обратный путь в изначальную точку вызова.

Если выброшенное исключение не находит catch, который его перехватит, исключение становится необработанным и приложение падает.

Эта логика работает очень строго и не ошибается. То есть если внутри по стеку вызовов от блока try было выброшено исключение, то catch (Exception) его поймает гарантированно.


Теперь почему казалось бы возникшее исключение казалось бы внутри стека вызовов может быть не перехвачено, например возьмём такой метод

async void Method()
{
    await Task.Delay(10);
    throw new Exception();
}

И вызовем его

try
{
    Method();
}
catch (Exception)
{
    // ...
}

Вот такое исключение не будет перехвачено, и вот почему:

  • Вызов метода Method()
  • Пуск асинхронного ожидания на 10мс, добавление в контекст синхронизации (SynchronizationContext) кусочка продолжения асинхронного метода
  • Выход в вызывающий метод
  • Завершение кода без ошибок и освобождение исполняющего потока
  • Контекст синхронизации переходит к следующей задаче и вызвает продолжение метода Method() со второй строки кода, то есть после строки, где await
  • Происходит выброс исключения
  • Продолжение выброса исключения в контекст синхронизации
  • Перехвата исключения в контексте нет, иcключение становится необработанным, приложение падает

То есть здесь блок catch хронологически будет пройден раньше, чем будет выброшено исключение. Решение - сделать метод async Task и добавить await Method(). Тогда catch уже в асинхронном контексте отработает корректно.

Отвечая прямо на вопрос - конструкция try-catch может поймать абсолютно любое исключение и обойти её невозможно. Те исключения, которые как кажется, что не ловятся, происходят в стеках вызовов, которые не начинаются из блока try, а из контекста синхронизации асинхронно. При этом если асихронный метод не возвращает Task, то исключение не будет захвачено в этот Task, а будет выброшено в вызывающий контекст и положит приложение. Другими словами, если вы замените в сигнатуре метода async void на async Task, а await не добавите при вызове, то исключение не даст о себе знать вообще, и будет выброшено в бездну, потому что оно будет захвачено внутри Task, который вы никак не используете.

Следовательно надо быть аккуратнее с асинхронными методами, нужно везде добавлять await и избегать async void. А там где избежать не получается, следует весь код внутри async void метода обернуть в try-catch(Exception).


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

→ Ссылка