Как лечить необработанные исключения? (Завершить приложение против сохранить его в живых)


30

Что лучше всего делать, когда в настольном приложении возникают необработанные исключения?

Я думал о том, чтобы показать сообщение пользователю, чтобы он мог связаться со службой поддержки. Я бы порекомендовал пользователю перезапустить приложение, но не заставлять его. Подобно тому, что обсуждается здесь: ux.stackexchange.com - Как лучше всего обрабатывать неожиданные ошибки приложения?

Проект представляет собой приложение .NET WPF, поэтому описанное предложение может выглядеть следующим образом (обратите внимание, что это упрощенный пример. Возможно, имеет смысл скрывать подробности исключения до тех пор, пока пользователь не нажмет «Показать подробности» и не предоставит некоторую функциональность легко сообщить об ошибке):

public partial class App : Application
{
    public App()
    {
        DispatcherUnhandledException += OnDispatcherUnhandledException;
    }

    private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        LogError(e.Exception);
        MessageBoxResult result = MessageBox.Show(
             $"Please help us fix it and contact support@example.com. Exception details: {e.Exception}" +
                        "We recommend to restart the application. " +
                        "Do you want to stop the application now? (Warning: Unsaved data gets lost).", 
            "Unexpected error occured.", MessageBoxButton.YesNo);

        // Setting 'Handled' to 'true' will prevent the application from terminating.
        e.Handled = result == MessageBoxResult.No;
    }

    private void LogError(Exception ex)
    {
        // Log to a log file...
    }
}

В реализации (Команды ViewModels или обработчик событий внешних событий) я бы тогда только поймал конкретное экзогенное исключение и позволил всем остальным исключениям (с головой и неизвестным исключениям) перейти к «Обработчику последней инстанции», описанному выше. Для определения исключений с головой и экзогенными исключениями взгляните на: Эрик Липперт - Исключительные исключения

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

Или это решение, если вам следует закрыть приложение на необработанные исключения в зависимости от типа приложения, которое вы пишете? Это просто компромисс между «надежностью» и «правильностью», как описано в Code Complete, Second Edition

Чтобы дать вам некоторое представление о том, о каком приложении мы говорим: Приложение в основном используется для управления химическими лабораторными приборами и отображения результатов измерений пользователю. Для этого приложения WPF взаимодействуют с некоторыми службами (локальными и удаленными службами). Приложение WPF не связывается напрямую с инструментами.


27
Если вы не ожидали исключения, как вы можете быть уверены, что приложение может безопасно продолжать работу?
дедупликатор

2
@Deduplicator: Конечно, вы не можете быть уверены. Как уже написано в качестве комментария к ответу Мэтью : «Да, конечно, приложение может быть в недопустимом состоянии. Может быть, некоторые ViewModel обновлены только частично. Но может ли это причинить какой-либо вред? Пользователь может перезагрузить данные, и если что-то будет недействительным, будет отправить в службу, тогда служба все равно не примет ее. Разве для пользователя не лучше, если он сможет сохранить данные до перезапуска приложения? "
Джонас Бенц

2
@ Voo Итак, вы уверены, что приложение может безопасно продолжать работу, всегда ожидая исключения? Кажется, вы отрицаете предпосылку получения неожиданного исключения.
дедупликатор

2
В любом случае, пожалуйста, сделайте сообщение об ошибке копируемым. Или, скажем, в каком файле журнала это было написано.
ComFreek

2
Обработка не обязательно подразумевает явное действие. Если вы можете быть уверены, что приложение может продолжаться безопасно, вы обработали исключение.
Чепнер

Ответы:


47

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

Начните с анализа следующих пунктов:

  • Сколько данных действительно может быть потеряно в случае завершения программы?

  • Насколько серьезна такая потеря для пользователя? Можно ли восстановить потерянные данные менее чем за 5 минут или речь идет о потере рабочего дня?

  • Сколько усилий нужно для реализации стратегии промежуточного резервного копирования? Не исключайте этого, потому что «пользователь должен будет ввести причину изменения» при обычной операции сохранения, как вы написали в комментарии. Лучше подумайте о чем-то вроде временного файла или состояния, которое может автоматически перезагружаться после сбоя программы. Многие типы производительного программного обеспечения делают это (например, MS Office и LibreOffice имеют функцию «автосохранения» и восстановления после сбоя).

  • Если данные были неправильными или поврежденными, сможет ли пользователь легко это увидеть (возможно, после перезапуска программы)? Если да, вы можете предложить опцию, позволяющую пользователю сохранять данные (с небольшой вероятностью, что они повреждены), затем принудительно перезапустить, перезагрузить их и позволить пользователю проверить, выглядят ли данные нормально. Не перезаписывайте последнюю версию, которая была сохранена регулярно (вместо записи во временное расположение / файл), чтобы не повредить старую версию.

Если такая стратегия «промежуточного резервного копирования» является разумным вариантом, в конечном итоге зависит от приложения и его архитектуры, а также от характера и структуры задействованных данных. Но если пользователь потеряет менее 10 минут работы, и такой сбой случается один раз в неделю или даже реже, я бы, наверное, не стал слишком много думать об этом.


10
en.wikipedia.org/wiki/Crash-only_software , и именно так приложения Android работают по необходимости.
Mooing Duck

3
Отличный ответ - и хороший пример рассмотрения вещей в более широком контексте (в данном случае «как мы можем предотвратить потерю данных в случае сбоя?») Приводит к лучшему решению.
слеске

1
Я сделал небольшое изменение, чтобы отметить, что вы не должны перезаписывать старые данные - надеюсь, вы не возражаете.
слеске

1
@MooingDuck Множество приложений для Android (например, игр) теряют свое состояние при сбое.
user253751

1
@immibis: Да, в Android действительно много приложений действительно низкого качества.
Mooing Duck

30

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

Зачем?

Потому что вы больше не можете быть уверены в состоянии приложения.

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

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


Я попытался добавить некоторую контекстную информацию о приложении в последней части.
Джонас Бенц

10
@JonasBenz Разве не лучше для пользователя, если он может сохранить, прежде чем он перезапустит приложение? Да, но как узнать, что данные, которые будет сохранять пользователь, действительны и не повреждены? На данный момент у вас есть неожиданное исключение, и вы действительно не знаете, почему. Ваш самый безопасный путь, хотя и раздражает пользователя, - это закрыть приложение. Если вы беспокоитесь о сохранении работы пользователя, вы должны использовать стратегию постоянного сохранения. Опять же, все зависит от приложения, которое вы пишете.
Матфея

4
Да, я могу поспорить так же: я не согласен с наличием кнопки «Продолжить». Проблема просто в том, что если вы, разработчик приложения, не знаете, можете ли вы продолжать безопасно, как пользователь может знать? Если вы получили необработанное исключение, это означает, что у вас есть ошибка, которую вы не ожидали, и вы не можете точно сказать, что происходит в этот момент. Пользователь захочет продолжить, потому что он не захочет потерять свою работу, я понимаю, но вы хотите, чтобы он продолжал, даже если ваше приложение могло давать плохие результаты из-за этой ошибки?
Матфея

3
@ Матфея «если вы, разработчик приложения, не знаете, можете ли вы продолжить безопасно, как пользователь может знать» , разработчик не знал, когда он писал код. Когда пользователь сталкивается с определенной ошибкой, подобной этой, это может быть известно. И пользователь может узнать из каких-либо пользовательских форумов, каналов поддержки и еще чего-нибудь, или просто протестировав и увидев, что происходит с его данными ... Я согласен, что это все еще слишком запутанно и опасно как функция пользователя, просто указывая, что время делает это пользователь может действительно знать, имеет ли смысл «продолжить» или нет.
Хайд

1
@JonasBenz, в Windows 3.1, диалоговое окно, которое появлялось, когда программа выполняла недопустимый доступ к памяти, имело кнопку «игнорировать», которая позволяла программе продолжать работу. Вы заметите, что каждая последующая версия Windows не имеет этой кнопки.
Марк

12

Учитывая, что это предназначено для химической лаборатории, и что ваше приложение не контролирует приборы напрямую, а через другие службы:

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

Но вы можете сделать что-то еще: изящно восстановиться после перезагрузки . Я предполагаю, что ваше приложение не отключает эти фоновые службы при сбое. В этом случае вы можете легко восстановить состояние от них. Или, если у вас больше состояния, подумайте о его сохранении. В хранилище, в котором предусмотрены атомарность и целостность данных (возможно, SQLite?).

Редактировать:

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


Завершение в состоянии, которое требует последующих команд ПРЯМО СЕЙЧАС, может быть столь же опасным в химической лаборатории.
Олег Васильевич Волков

@ OlegV.Volkov так может быть перезапустишь себя по окончании? На приличном компьютере запуск графического интерфейса должен занимать порядка сотен миллисекунд. Если процесс требует более жестких таймингов, управление не будет реализовано в ОС не в реальном времени. Хотя это ОП, кто должен сделать окончательную оценку риска.
Ян Дорняк

@ OlegV.Volkov это хорошая мысль, поэтому я добавил свое мнение в ответ.
Ян Дорняк

8

Попытка вообще ответить на этот вопрос на верхнем уровне программы не является умной игрой.

Если что-то все время пузырилось, и ни в одной точке архитектуры приложения никто не рассматривал этот случай, у вас нет никаких обобщений, которые вы можете сделать о том, какие действия безопасны или нет.

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

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

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

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


К сожалению, многие методы для таких вещей, как «Создание объекта с данными, полученными из некоторого местоположения», не делают различий между исключениями, которые указывают, что действие не может быть выполнено, но попытка не имела побочных эффектов, в отличие от тех, которые указывают, что что-то более серьезное пошло не так Тот факт, что попытка загрузить ресурс не удалась по какой-то причине, которую никто не ожидал, не должна приводить к фатальной ошибке, если вы, как правило, готовы к неспособности построить объект. Важны побочные эффекты, которые, к сожалению, игнорируются структурами исключений.
суперкат

@supercat - если вы можете определить ошибку, вы можете обработать ее. Если вы не можете его идентифицировать, вы не сможете его обработать, если только вы не напишите процедуру для проверки целостности состояния приложения, чтобы попытаться выяснить, что могло пойти не так. Неважно, в чем ошибка «могла быть», мы прямо установили, что не знаем этого из-за того, что пытаемся обрабатывать необработанные исключения.
Железный Кремль

3

Проблема с «исключительными исключениями», то есть исключениями, которые вы не предвидели, заключается в том, что вы не знаете, в каком состоянии находится программа. Например, попытка сохранить данные пользователя может фактически уничтожить еще больше данных .

По этой причине вы должны закрыть приложение.

Есть очень интересная идея под названием Crash- Only Software от Джорджа Кандеа и Армандо Фокса . Идея состоит в том, что если вы разрабатываете свое программное обеспечение таким образом, что единственный способ закрыть его - это его аварийное завершение, а единственный способ запустить его - это восстановление после сбоя, тогда ваше программное обеспечение будет более устойчивым и устранение ошибок. пути кода будут намного более тщательно проверены и реализованы.

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

Хорошим примером, хотя он больше не актуален, являются некоторые старые версии Firefox, которые не только запускаются быстрее при восстановлении после сбоя, но и имеют лучший способ запуска при этом ! В этих версиях, если вы нормально выключите Firefox, он закроет все открытые вкладки и запустит одну пустую вкладку. Принимая во внимание, что при восстановлении после сбоя он восстановит открытые вкладки во время сбоя. (И это был единственный способ закрыть Firefox без потери текущего контекста просмотра.) Итак, что же делали люди? Они просто никогда не закрывали Firefox и вместо этого всегда pkill -KILL firefoxредактировали его.

В блоге Linux Weekly News есть отличная статья о программном обеспечении, предназначенном только для сбоев, Валери Аврора . Комментарии также стоит прочитать. Например, кто-то в комментариях справедливо указывает, что эти идеи не новы и фактически более или менее эквивалентны принципам разработки приложений на основе Erlang / OTP. И, конечно, если взглянуть на это сегодня, еще через 10 лет после Валери и через 15 лет после оригинальной статьи, мы могли бы заметить, что нынешняя ажиотаж в сфере микро-услуг вновь изобретает те же идеи. Современный облачный дизайн центров обработки данных также является примером более грубой детализации. (Любой компьютер может выйти из строя в любое время, не влияя на систему.)

Однако этого недостаточно, чтобы просто позволить вашему программному обеспечению рухнуть. Это должно быть разработано для этого. В идеале ваше программное обеспечение должно быть разбито на небольшие независимые компоненты, каждый из которых может зависать независимо. Кроме того, «механизм сбоя» должен находиться за пределами компонента, в котором происходит сбой.


1

Правильный способ обработки большинства исключений должен состоять в том, чтобы сделать недействительным любой объект, который в результате может быть поврежден, и продолжить выполнение, если недействительные объекты не предотвращают это. Например, безопасная парадигма для обновления ресурса:

acquire lock
try
  update guarded resource
if exception
  invalidate lock
else
  release lock
end try

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

К сожалению, средства защиты ресурсов, реализованные через IDisposable/, usingбудут освобождаться всякий раз, когда защищаемый блок выходит, без какого-либо способа узнать, вышел ли блок нормально или ненормально. Таким образом, несмотря на то, что должны быть четко определенные критерии того, когда продолжать работу после исключения, невозможно определить, когда они применяются.


+1 просто для выражения этой относительно неочевидной и все еще не распространенной точки зрения на то, каков правильный путь. На самом деле я пока не знаю, согласен ли я с этим, потому что для меня это новое эвристическое правило, так что мне нужно немного обдумать его, но это кажется правдоподобным.
mtraceur

0

Вы можете использовать подход, который следует каждому приложению для iOS и MacOS: необработанное исключение немедленно удаляет приложение. Плюс многие ошибки, такие как выход за пределы массива или просто арифметическое переполнение в новых приложениях, делают то же самое. Нет предупреждения

По моему опыту, многие пользователи не обращают на это никакого внимания, а просто снова нажимают на значок приложения.

Очевидно, что вам нужно убедиться, что такой сбой не приведет к значительной потере данных и определенно не приведет к дорогостоящим ошибкам. Но предупреждение «Ваше приложение будет зависать сейчас. Позвоните в службу поддержки, если это вас беспокоит »никому не помогает.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.