Обработка ошибок - в случае сбоя программы при ошибках или игнорирования их


20

Я пишу простую небольшую программу для передачи MIDI по сети. Я знаю, что программа столкнется с проблемами передачи и / или другими исключительными ситуациями, которые я не смогу предсказать.

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

  • терпит неудачу с ударом, когда что-то идет не так или
  • Должен ли он просто игнорировать ошибку и продолжать, за счет целостности данных?

Какой подход разумно ожидать от пользователя?
Есть ли лучший способ обработки исключений?

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



Есть ли способ +1 редактирования? ;) Ой, подожди, я пойду за тобой. Конечно, сделаем :)
Арлен Бейлер

2
«Какой подход разумно ожидать от пользователя?». Вы пытались спросить одного из ваших пользователей? Юзабилити-тестирование прихожей может быть очень эффективным. См. Blog.openhallway.com/?p=146
Брайан Оукли,

У меня нет пользователей :)
Arlen Beiler

Ответы:


34

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

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


18
Недостаточно, чтобы оправдать -1, но «никогда» не является сильным словом, и бывают ситуации, когда игнорирование ошибок является правильным курсом действий. Например, программное обеспечение для декодирования вещательных передач HDTV. Когда вы сталкиваетесь с некоторыми ошибками, которые вы часто делаете, все, что вы можете сделать, это игнорировать их и продолжать декодировать последующие события.
whatsisname

1
@whatsisname: правда, но вы должны четко задокументировать, почему вы его игнорируете. Обновленный ответ, чтобы рассмотреть ваш комментарий.
marco-fiset

1
@ marco-fiset Я не согласен. Сбой всей программы - это только решение проблемы, если ее перезапуск решит проблему. Это не всегда так, поэтому разбивать его часто бессмысленно и по умолчанию такое поведение просто глупо. Почему вы хотите приостановить всю операцию из-за локализованной ошибки? Это бессмысленно. Большинство приложений делают это, и, по какой-то причине, программисты полностью согласны с этим.
MaiaVictor

@Dokkat: смысл в том, чтобы не продолжать с неправильными значениями, что даст неправильное решение (мусор входит, мусор выходит). Сбой действительно решает проблему: он вынуждает пользователя либо предоставлять различные входные данные, либо приставлять разработчику к исправлению их программы;)
Андре Парамес,

1
@whatsisname Я думаю, вы, возможно, захотите подумать над своим определением «ошибка» в этом случае. Ошибки в полученных данных - это не то же самое, что ошибки в структуре и выполнении программного обеспечения.
joshin4colours

18

Вы никогда не должны молча игнорировать ошибки, потому что ваша программа построена на серии действий, которые неявным образом зависят от всего, что было сделано до их исправления. Если на шаге 3 что-то пойдет не так, и вы попытаетесь перейти к шагу 4, шаг 4 будет начинаться на основе неверных предположений, что повышает вероятность того, что он также приведет к возникновению ошибки. (И если вы проигнорируете это тоже, то шаг 5 выдает ошибку, и оттуда вещи начнут расти как снежный ком.)

Дело в том, что по мере накопления ошибок в конечном итоге вы столкнетесь с такой большой ошибкой, что не сможете ее игнорировать, потому что она будет состоять из чего-то, что дается пользователю, и что это будет совершенно неправильно. Тогда у вас есть пользователи, которые жалуются на то, что ваша программа не работает должным образом, и вы должны это исправить. И если часть «дать что-то пользователю» находится на шаге 28, и вы не представляете, что первоначальная ошибка, которая привела ко всему этому беспорядку, была на шаге 3, потому что вы проигнорировали ошибку на шаге 3, вы получите черт возьми, отладка проблемы!

С другой стороны, если эта ошибка на шаге 3 приводит к тому, что все вспыхивает на лице пользователя, и генерирует ошибку, говоря SOMETHING WENT BADLY WRONG IN STEP 3!(или ее технический эквивалент, трассировка стека), то результат будет тем же - пользователь жаловался на вас программа не работает должным образом - но на этот раз вы точно знаете, с чего начать, когда будете ее исправлять .

РЕДАКТИРОВАТЬ: В ответ на комментарии, если что-то идет не так, как вы ожидали и знаете, как справиться, это другое. Например, в случае получения искаженного сообщения это не является ошибкой программы; это «пользователь предоставил неверный ввод, который не прошел проверку». Соответствующий ответ - сообщить пользователю, что он вводит вам недопустимые данные, как, по вашему мнению, вы и делаете. Нет необходимости аварийно завершать работу и генерировать трассировку стека в таком случае.


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

@Arlen: Даже это может быть плохой идеей. Ошибки возникают из-за того, что произошло то, чего вы не ожидали, когда кодировали его . Это означает, что все ваши предположения ушли в окно. Эта «последняя известная хорошая позиция» может больше не быть хорошей, если вы уже на полпути изменили какую-то структуру данных и теперь, например, она находится в несогласованном состоянии.
Мейсон Уилер

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

@Arlen: Если вы предвидели это и уверены, что знаете, каковы последствия ошибки и правильно ли они содержатся, то это другое. Похоже, вы обрабатываете ошибку и реагируете соответствующим образом, а не игнорируете ее. Я говорил об игнорировании неожиданных ошибок, о чем вы и говорили .
Мейсон Уилер

16

Есть и другие варианты между «взорвать» и «игнорировать».

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

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

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

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

Как правило, не поймайте никаких исключений, с которыми вы ничего не можете сделать, если только вы не планируете регистрировать и перебрасывать их. И в тех редких случаях, когда try-catch-ignore неизбежен, по крайней мере, добавьте комментарий в свой блок catch, чтобы объяснить, почему.

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


1
Лучший ответ на данный момент, на мой взгляд.
thursdaysgeek

6

Вот мои взгляды на вопрос:

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

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

Быстро и медленно покрывая все более и более конкретные ошибки, вы никогда не нарушите целостность данных и неуклонно продвигаетесь к более стабильной программе.

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

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


6

Вы никогда не должны молча игнорировать ошибки. И особенно не за счет целостности данных .

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

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

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

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


Вы действительно хотели поставить вопросительный знак в конце первой строки?
CVn

@ MichaelKjörling: Нет. Ошибка копирования-вставки (скопировала формулировку из вопроса и включила вопросительный знак по ошибке).
Ян Худек

4

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

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


1

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

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

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

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

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

Я считаю, что если никто не знает об ошибке - она ​​никогда не будет исправлена. Я также твердо верю в обработку ошибок, которая позволяет приложению продолжать функционировать после обнаружения ошибки.

Если ошибка связана с сетью - почему бы не выполнить функции простого теста сетевой связи перед выполнением этой функции, чтобы избежать ошибки в первую очередь? Затем просто предупредите пользователя о том, что соединение не доступно, проверьте ваш интернет и т. Д. И т. Д. И попробуйте еще раз?


1
Я лично провожу много проверок перед запуском критических функций, чтобы попытаться ограничить ошибки, которые я до конца не понимаю и не могу обеспечить полезную обработку ошибок, которая возвращает подробную информацию. Это не работает в 100% случаев, но я не могу вспомнить, когда в последний раз у меня была ошибка, из-за которой я потерял несколько часов.
Джефф

1

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

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

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

Для ошибок времени выполнения, которые могут произойти с кодом без ошибок, таким как сбои сети или последовательной связи или отсутствующие или поврежденные файлы:

  1. Зарегистрируйте ошибку.
  2. (Необязательно). Попытка выполнить повторную попытку или восстановление иным образом после операции.
  3. Если операция продолжает завершаться сбоем или не восстанавливается, визуально сообщите об ошибке пользователю. Затем, как указано выше, пользователь может решить, что делать. Помните принцип наименьшего удивления , потому что потеря целостности данных удивляет пользователя, если вы не предупредили их заранее.

0

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

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