Як поводитися з неушкодженими винятками? (Завершіть програму проти збереження в живих)


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
@ Дедуплікатор: Звичайно, ти не можеш бути впевнений. Як уже написано як коментар до відповіді Метью : "Так, звичайно, програма може бути в недійсному стані. Можливо, деякі ViewModel оновились лише частково. Але чи може це нашкодити? Користувач може перезавантажити дані, і якщо щось недійсне буде відправте в службу, тоді служба все одно не прийме її. Хіба не краще для користувача, якщо він може зберегти, перш ніж перезапустити програму? "
Йонас Бенц

2
@Voo Отже, ви впевнені, що програма може безпечно продовжувати роботу, завжди очікуючи винятку? Здається, ви заперечуєте припущення отримати несподіваний виняток.
Дедуплікатор

2
У будь-якому випадку, будь ласка, зробіть повідомлення про помилку копіюваним. Крім того, скажіть, у якому файлі журналу це записано.
ComFreek

2
Поводження не обов'язково означає явні дії. Якщо ви можете бути впевнені, що програма може продовжуватись безпечно, ви вирішили виняток.
чепнер

Відповіді:


47

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

Почніть з аналізу наступних моментів:

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

  • Наскільки насправді така втрата дійсно для користувача? Чи можна відновити втрачені дані менш ніж за 5 хвилин, або ми говоримо про втрату днів?

  • Скільки зусиль доводиться реалізувати якусь стратегію "проміжного резервного копіювання"? Не виключайте цього, оскільки "користувачеві доведеться вводити причину зміни" під час звичайної операції збереження, як ви писали в коментарі. Краще придумайте щось на зразок тимчасового файлу або стану, яке може бути перезавантажено після аварії програми автоматично. Це робить багато типів програмного забезпечення для продуктивності (наприклад, MS Office та LibreOffice мають функцію "автоматичного збереження" та відновлення після аварій).

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

Якщо така стратегія "проміжного резервного копіювання" є розумним варіантом, в кінцевому рахунку залежить від програми та його архітектури, а також від характеру та структури залучених даних. Але якщо користувач втратить менше 10 хвилин роботи, і такий збій трапляється раз на тиждень або навіть рідше, я, певно, не вкладав би в це занадто багато думок.


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

3
Відмінна відповідь - і приємний приклад розгляду речей у більш широкому контексті (у цьому випадку "як ми можемо запобігти втраті даних у будь-якому випадку збоїв?") Призводить до кращого рішення.
sleske

1
Я зробив невелику редакцію, щоб зазначити, що не слід перезаписувати старі дані - сподіваюся, ви не заперечуєте.
sleske

1
@MooingDuck Багато додатків для Android (наприклад, ігор) втрачають стан при аварії.
користувач253751

1
@immibis: Так, Android справді має велику кількість дійсно низькоякісних додатків.
Mooing Duck

30

Якоюсь мірою це залежить від програми, яку ви розробляєте, але в цілому, я б сказав, що якщо ваша програма стикається з необробленим винятком, вам потрібно її скасувати.

Чому?

Тому що ви більше не можете мати впевненості в стані програми.

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

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


Я спробував додати деяку контекстну інформацію про програму в останній частині.
Йонас Бенц

10
@JonasBenz Чи не краще це користувачеві, якщо він здатний економити перед тим, як перезапустити програму? Так, але як ви знаєте, чи дані, які користувач зберігає, є дійсними та не пошкодженими? На даний момент у вас з’явився несподіваний виняток, і ви дійсно не знаєте чому. Ваш найбезпечніший шлях, хоч і дратує користувача, - це припинення роботи програми. Якщо ви стурбовані роботою щодо збереження користувачів, ви використовуєте стратегію постійного збереження. Знову ж таки, все залежить від програми, яку ви пишете.
Метью

4
Так, я можу стверджувати так само і тут: я не згоден з наявністю кнопки «Продовжити». Проблема полягає лише в тому, що якщо ви, розробник програми, не знаєте, чи можете ви продовжувати безпечно, як це може знати користувач? Якщо ви отримаєте необроблений виняток, це означає, що у вас є помилка, якої ви не очікували, і ви не можете точно сказати, що відбувається в цей момент. Користувач захоче продовжувати роботу, оскільки він не захоче втрачати свою роботу, я розумію це, але чи хочете ви дозволити їм продовжуватись, навіть якщо ваша програма може призвести до поганих результатів через цю помилку?
Метью

3
@Matthew "якщо ви, розробник програми, не знаєте, чи можете ви продовжувати безпечно, як це може знати користувач" , розробник не знав, коли вони писали код. Коли користувач стикається з конкретною помилкою, подібною до цього, це може бути відомо. І користувач може дізнатися з будь-яких користувацьких форумів, каналів підтримки чи чогось іншого, або просто випробувавши та побачивши, що відбувається з їхніми даними ... Я згоден, що це все ще занадто незрозуміло та небезпечно як користувацька функція, лише зазначаючи, що час робить це можливо, щоб користувач дійсно знав, чи є сенс «продовжити» чи ні.
Гайд

1
@JonasBenz, під Windows 3.1, у діалоговому вікні, яке з’явилося, коли програма здійснювала незаконне доступ до пам'яті, була кнопка «ігнорувати», яка дозволила програмі продовжувати працювати. Ви зауважите, що в кожній наступній версії Windows немає цієї кнопки.
Марк

12

Враховуючи, що це призначено для хімічної лабораторії та що ваша програма не контролює інструменти безпосередньо, а через інші служби:

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

Але ви можете зробити щось інше: витончено відновіться після перезавантаження . Я припускаю, що ваша програма не збиває цих фонових служб при собі, коли вона виходить з ладу. У такому випадку ви можете легко відновити стан з них. Або, якщо у вас більше штату, подумайте про збереження. У сховищі, яке містить положення про атомність та цілісність даних (можливо SQLite?).

Редагувати:

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


Припинення в стані, яке вимагає подальших команд ПРАВО ЗАРАЗ, може бути таким же небезпечним у хімічній лабораторії.
Олег Вікторович Волков

@ Олег В.Волков, може, перезавантажте себе після припинення? На гідному комп’ютері, який запускає графічний інтерфейс, слід набирати порядок сотень мілісекунд. Якщо процес вимагає більш жорстких термінів, контроль не здійснюватиметься в операційній системі не в реальному часі. Хоча саме ОП повинен зробити остаточну оцінку ризику.
Ян Дорняк

@ Олег В.Волков, це хороший момент, тому я додав свою думку про це у відповідь.
Ян Дорняк

8

Намагання взагалі відповісти на це питання на найвищому рівні програми - це не розумна гра.

Якщо щось повстало, і в жодній точці архітектури програми хтось не розглядав цей випадок, у вас немає узагальнень, які можна зробити про те, які дії чи ні, безпечні для здійснення.

Отже, ні, це, безумовно, не є загальноприйнятною конструкцією, яка дозволяє користувачеві вибирати, чи намагається програма відновити програму, оскільки програма та розробники демонстративно не зробили належну ретельність, необхідну для з'ясування, чи це можливо чи навіть розумно .

Однак якщо у додатку є важливі частини логіки чи поведінки, які були розроблені на увазі подібного роду відновлення несправностей, і в цьому випадку можливо використовувати їх, то, без сумніву, зробіть це - У такому випадку , можливо, буде прийнятним спонукати користувача побачити, чи хоче він спробувати відновити, чи хотів би просто зателефонувати йому, що закривається, і почати спочатку.

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

За будь-якої спеціальної логіки відновлення відмов - Ні, не робіть цього. Ви буквально не маєте уявлення про те, що буде, якби ви це зробили, ви б пізніше вловили виняток і вирішили це.


На жаль, багато методів для таких речей, як "Створення об'єкта з даними, отриманими з якогось місця", не роблять різниці між винятками, які вказують на те, що дію не вдалося виконати, але спроба не мала побічних ефектів, порівняно з тими, що свідчать про щось більш серйозне пішов не так. Той факт, що спроба завантажити ресурс не вдалася з якихось причин, яких не передбачалося, не повинно викликати фатальну помилку, якщо вона загалом готується до неможливості побудови об'єкта. Важливими є побічні ефекти, які, на жаль, є чимось виключенням рамки виключення.
supercat

@supercat - якщо ви зможете виявити помилку, ви можете її виправити. Якщо ви не можете його ідентифікувати, ви не можете впоратися з ним, якщо ви не напишете процедуру, щоб запустити перевірку цілісності стану програми, щоб спробувати визначити, що може піти не так. Не має значення, яка помилка "могла бути", ми чітко встановили, що не знаємо цього через те, що намагаємось в основному обробляти виняткові винятки.
Залізний Гремль

3

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

З цієї причини вам слід скасувати заявку.

Існує дуже цікава ідея, яку Джордж Кандеа та Армандо Фокс називають програмним забезпеченням , що розбиваються тільки на аварію . Ідея полягає в тому, що якщо ви розробляєте програмне забезпечення таким чином, що єдиний спосіб його закрити - це його збій, і єдиний спосіб запустити його - відновити після аварії, то ваше програмне забезпечення буде більш стійким і відновлення помилок кодові шляхи будуть набагато ретельніше перевірені та використані.

Вони придумали цю ідею після того, як помітили, що деякі системи запускаються швидше після аварії, ніж після впорядкованого відключення.

Хороший, хоча вже не актуальний приклад - деякі старіші версії 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.